From fb1e82d6f3091c97fc205d568ad189aa1425c003 Mon Sep 17 00:00:00 2001
From: John Lindsay
Date: Mon, 9 Dec 2019 11:29:43 -0500
Subject: [PATCH] prep for Version 1.0.3
prep for Version 1.0.3
---
.DS_Store | Bin 10244 -> 10244 bytes
Cargo.lock | 8 +-
Cargo.toml | 4 +-
build.py | 70 -
lib_test.py | 45 -
readme.txt | 30 +
src/algorithms/delaunay_triangulation.rs | 55 +
src/raster/arcascii_raster.rs | 2 +
src/raster/arcbinary_raster.rs | 2 +
src/raster/geotiff/geokeys.rs | 18 +-
src/raster/geotiff/tiff_consts.rs | 160 +-
src/raster/grass_raster.rs | 2 +
src/raster/idrisi_raster.rs | 6 +-
src/raster/mod.rs | 2 +-
src/raster/saga_raster.rs | 31 +-
src/raster/surfer7_raster.rs | 2 +
src/raster/surfer_ascii_raster.rs | 1 +
src/raster/whitebox_raster.rs | 8 +-
src/structures/array2d.rs | 22 +-
src/structures/mod.rs | 4 +-
src/structures/radial_basis_function.rs | 143 +
.../add_point_coordinates_to_table.rs | 2 +-
src/tools/data_tools/clean_vector.rs | 2 +-
.../data_tools/convert_nodata_to_zero.rs | 2 +-
src/tools/data_tools/convert_raster_format.rs | 2 +-
src/tools/data_tools/csv_points_to_vector.rs | 2 +-
src/tools/data_tools/export_table_to_csv.rs | 2 +-
src/tools/data_tools/join_tables.rs | 2 +-
src/tools/data_tools/lines_to_polygons.rs | 2 +-
src/tools/data_tools/merge_table_with_csv.rs | 2 +-
src/tools/data_tools/merge_vectors.rs | 2 +-
src/tools/data_tools/modify_nodata_value.rs | 2 +-
.../data_tools/multipart_to_singlepart.rs | 2 +-
src/tools/data_tools/new_raster.rs | 2 +-
src/tools/data_tools/polygons_to_lines.rs | 2 +-
src/tools/data_tools/print_geotiff_tags.rs | 2 +-
.../data_tools/raster_to_vector_lines.rs | 2 +-
.../data_tools/raster_to_vector_points.rs | 2 +-
.../reinitialize_attribute_table.rs | 2 +-
src/tools/data_tools/remove_polygon_holes.rs | 2 +-
src/tools/data_tools/set_nodata_value.rs | 2 +-
.../data_tools/singlepart_to_multipart.rs | 2 +-
.../data_tools/vector_lines_to_raster.rs | 2 +-
.../data_tools/vector_points_to_raster.rs | 2 +-
.../data_tools/vector_polygons_to_raster.rs | 2 +-
src/tools/gis_analysis/aggregate_raster.rs | 2 +-
src/tools/gis_analysis/average_overlay.rs | 2 +-
src/tools/gis_analysis/block_maximum.rs | 7 +-
src/tools/gis_analysis/block_minimum.rs | 7 +-
.../gis_analysis/boundary_shape_complexity.rs | 2 +-
src/tools/gis_analysis/buffer_raster.rs | 2 +-
src/tools/gis_analysis/buffer_vector.rs | 2 +-
src/tools/gis_analysis/centroid.rs | 2 +-
src/tools/gis_analysis/centroid_vector.rs | 2 +-
src/tools/gis_analysis/clip.rs | 7 +-
.../gis_analysis/clip_raster_to_polygon.rs | 2 +-
src/tools/gis_analysis/clump.rs | 3 +-
src/tools/gis_analysis/compactness_ratio.rs | 2 +-
.../gis_analysis/construct_vector_tin.rs | 164 +-
src/tools/gis_analysis/cost_allocation.rs | 4 +-
src/tools/gis_analysis/cost_distance.rs | 4 +-
src/tools/gis_analysis/cost_pathway.rs | 2 +-
src/tools/gis_analysis/count_if.rs | 2 +-
.../create_hexagonal_vector_grid.rs | 2 +-
src/tools/gis_analysis/create_plane.rs | 2 +-
.../create_rectangular_vector_grid.rs | 2 +-
src/tools/gis_analysis/difference.rs | 14 +-
src/tools/gis_analysis/dissolve.rs | 6 +-
src/tools/gis_analysis/edge_proportion.rs | 2 +-
.../eliminate_coincident_points.rs | 2 +-
src/tools/gis_analysis/elongation_ratio.rs | 2 +-
src/tools/gis_analysis/erase.rs | 7 +-
.../gis_analysis/erase_polygon_from_raster.rs | 2 +-
.../gis_analysis/euclidean_allocation.rs | 2 +-
src/tools/gis_analysis/euclidean_distance.rs | 106 +-
src/tools/gis_analysis/extend_vector_lines.rs | 2 +-
src/tools/gis_analysis/extract_nodes.rs | 2 +-
.../extract_raster_values_at_points.rs | 2 +-
.../find_lowest_or_highest_points.rs | 2 +-
.../gis_analysis/find_patch_edge_cells.rs | 2 +-
src/tools/gis_analysis/highest_pos.rs | 2 +-
src/tools/gis_analysis/hole_proportion.rs | 2 +-
src/tools/gis_analysis/idw_interpolation.rs | 21 +-
src/tools/gis_analysis/intersect.rs | 13 +-
src/tools/gis_analysis/layer_footprint.rs | 2 +-
src/tools/gis_analysis/line_intersections.rs | 2 +-
src/tools/gis_analysis/linearity_index.rs | 2 +-
src/tools/gis_analysis/lowest_pos.rs | 2 +-
src/tools/gis_analysis/max_abs_overlay.rs | 2 +-
src/tools/gis_analysis/max_overlay.rs | 2 +-
src/tools/gis_analysis/medoid.rs | 2 +-
src/tools/gis_analysis/merge_line_segments.rs | 4 +-
src/tools/gis_analysis/min_abs_overlay.rs | 2 +-
src/tools/gis_analysis/min_overlay.rs | 2 +-
.../gis_analysis/minimum_bounding_box.rs | 2 +-
.../gis_analysis/minimum_bounding_circle.rs | 2 +-
.../gis_analysis/minimum_bounding_envelope.rs | 2 +-
src/tools/gis_analysis/minimum_convex_hull.rs | 2 +-
src/tools/gis_analysis/mod.rs | 2 +
src/tools/gis_analysis/narrowness_index.rs | 2 +-
.../natural_neighbour_interpolation.rs | 727 ++
.../nearest_neighbour_gridding.rs | 13 +-
src/tools/gis_analysis/patch_orientation.rs | 2 +-
src/tools/gis_analysis/percent_equal_to.rs | 2 +-
.../gis_analysis/percent_greater_than.rs | 2 +-
src/tools/gis_analysis/percent_less_than.rs | 2 +-
.../gis_analysis/perimeter_area_ratio.rs | 2 +-
src/tools/gis_analysis/pick_from_list.rs | 2 +-
src/tools/gis_analysis/polygon_area.rs | 2 +-
src/tools/gis_analysis/polygon_long_axis.rs | 2 +-
src/tools/gis_analysis/polygon_perimeter.rs | 2 +-
src/tools/gis_analysis/polygon_short_axis.rs | 2 +-
src/tools/gis_analysis/polygonize.rs | 4 +-
src/tools/gis_analysis/radius_of_gyration.rs | 2 +-
src/tools/gis_analysis/raster_area.rs | 14 +-
.../gis_analysis/raster_cell_assignment.rs | 2 +-
src/tools/gis_analysis/raster_perimeter.rs | 526 ++
src/tools/gis_analysis/reclass.rs | 2 +-
.../gis_analysis/reclass_equal_interval.rs | 2 +-
src/tools/gis_analysis/reclass_from_file.rs | 2 +-
.../related_circumscribing_circle.rs | 2 +-
.../gis_analysis/shape_complexity_index.rs | 2 +-
.../gis_analysis/shape_complexity_raster.rs | 2 +-
.../gis_analysis/sibson_interpolation.rs | 2 +-
src/tools/gis_analysis/smooth_vectors.rs | 2 +-
src/tools/gis_analysis/snap_endnodes.rs | 2 +-
src/tools/gis_analysis/split_with_lines.rs | 4 +-
src/tools/gis_analysis/sum_overlay.rs | 2 +-
.../gis_analysis/symmetrical_difference.rs | 12 +-
src/tools/gis_analysis/tin_gridding.rs | 124 +-
src/tools/gis_analysis/union.rs | 18 +-
src/tools/gis_analysis/vector_hex_bin.rs | 2 +-
src/tools/gis_analysis/voronoi_diagram.rs | 9 +-
src/tools/gis_analysis/weighted_overlay.rs | 2 +-
src/tools/gis_analysis/weighted_sum.rs | 2 +-
.../hydro_analysis/average_flowpath_slope.rs | 2 +-
.../average_upslope_flowpath_length.rs | 2 +-
src/tools/hydro_analysis/basins.rs | 2 +-
.../hydro_analysis/breach_depressions.rs | 25 +-
.../breach_depressions_least_cost.rs | 1181 +++
src/tools/hydro_analysis/breach_pits.rs | 2 +-
.../hydro_analysis/burn_streams_at_roads.rs | 4 +-
src/tools/hydro_analysis/d8_flow_accum.rs | 2 +-
src/tools/hydro_analysis/d8_mass_flux.rs | 2 +-
src/tools/hydro_analysis/d8_pointer.rs | 2 +-
src/tools/hydro_analysis/depth_in_sink.rs | 773 +-
src/tools/hydro_analysis/dinf_flow_accum.rs | 2 +-
src/tools/hydro_analysis/dinf_mass_flux.rs | 2 +-
src/tools/hydro_analysis/dinf_pointer.rs | 2 +-
.../downslope_distance_to_stream.rs | 2 +-
.../downslope_flowpath_length.rs | 2 +-
.../hydro_analysis/elevation_above_stream.rs | 2 +-
.../elevation_above_stream_euclidean.rs | 2 +-
src/tools/hydro_analysis/fd8_flow_accum.rs | 81 +-
src/tools/hydro_analysis/fd8_pointer.rs | 2 +-
src/tools/hydro_analysis/fill_burn.rs | 7 +-
src/tools/hydro_analysis/fill_depressions.rs | 783 +-
.../fill_depressions_wang_and_lui.rs | 653 ++
src/tools/hydro_analysis/fill_pits.rs | 4 +-
src/tools/hydro_analysis/find_noflow_cells.rs | 6 +-
.../hydro_analysis/find_parallel_flow.rs | 2 +-
src/tools/hydro_analysis/flatten_lakes.rs | 2 +-
src/tools/hydro_analysis/flood_order.rs | 2 +-
.../flow_accum_full_workflow.rs | 10 +-
src/tools/hydro_analysis/flow_length_diff.rs | 2 +-
src/tools/hydro_analysis/hillslopes.rs | 2 +-
src/tools/hydro_analysis/impoundment_index.rs | 2 +-
src/tools/hydro_analysis/isobasins.rs | 2 +-
.../hydro_analysis/jenson_snap_pour_points.rs | 2 +-
src/tools/hydro_analysis/longest_flowpath.rs | 2 +-
.../hydro_analysis/max_upslope_flowpath.rs | 2 +-
src/tools/hydro_analysis/mod.rs | 6 +
.../num_inflowing_neighbours.rs | 2 +-
src/tools/hydro_analysis/raise_walls.rs | 2 +-
src/tools/hydro_analysis/rho8_pointer.rs | 2 +-
src/tools/hydro_analysis/sink.rs | 353 +-
src/tools/hydro_analysis/snap_pour_points.rs | 2 +-
.../stochastic_depression_analysis.rs | 2 +-
src/tools/hydro_analysis/strahler_basins.rs | 2 +-
src/tools/hydro_analysis/subbasins.rs | 2 +-
.../trace_downslope_flowpaths.rs | 2 +-
src/tools/hydro_analysis/unnest_basins.rs | 2 +-
.../upslope_depression_storage.rs | 597 ++
src/tools/hydro_analysis/watershed.rs | 2 +-
src/tools/image_analysis/adaptive_filter.rs | 2 +-
.../balance_contrast_enhancement.rs | 2 +-
src/tools/image_analysis/bilateral_filter.rs | 2 +-
.../image_analysis/change_vector_analysis.rs | 2 +-
src/tools/image_analysis/closing.rs | 2 +-
.../conservative_smoothing_filter.rs | 2 +-
src/tools/image_analysis/corner_detection.rs | 2 +-
.../image_analysis/correct_vignetting.rs | 2 +-
.../image_analysis/create_colour_composite.rs | 2 +-
.../direct_decorrelation_stretch.rs | 2 +-
src/tools/image_analysis/diversity_filter.rs | 2 +-
src/tools/image_analysis/dog_filter.rs | 2 +-
.../edge_preserving_mean_filter.rs | 2 +-
src/tools/image_analysis/emboss_filter.rs | 2 +-
.../fast_almost_gaussian_filter.rs | 2 +-
src/tools/image_analysis/flip_image.rs | 2 +-
src/tools/image_analysis/gamma_correction.rs | 2 +-
.../gaussian_contrast_stretch.rs | 2 +-
src/tools/image_analysis/gaussian_filter.rs | 2 +-
src/tools/image_analysis/highpass_filter.rs | 2 +-
.../image_analysis/highpass_median_filter.rs | 2 +-
.../image_analysis/histogram_equalization.rs | 2 +-
.../image_analysis/histogram_matching.rs | 2 +-
.../histogram_matching_two_images.rs | 2 +-
src/tools/image_analysis/ihs_to_rgb.rs | 2 +-
.../image_analysis/image_stack_profile.rs | 2 +-
src/tools/image_analysis/integral_image.rs | 2 +-
.../image_analysis/k_means_clustering.rs | 2 +-
.../image_analysis/k_nearest_mean_filter.rs | 2 +-
src/tools/image_analysis/laplacian_filter.rs | 2 +-
src/tools/image_analysis/lee_filter.rs | 2 +-
.../image_analysis/line_detection_filter.rs | 2 +-
src/tools/image_analysis/line_thin.rs | 2 +-
src/tools/image_analysis/log_filter.rs | 2 +-
src/tools/image_analysis/majority_filter.rs | 2 +-
src/tools/image_analysis/max_filter.rs | 2 +-
src/tools/image_analysis/mean_filter.rs | 2 +-
src/tools/image_analysis/median_filter.rs | 2 +-
src/tools/image_analysis/min_filter.rs | 2 +-
.../min_max_contrast_stretch.rs | 2 +-
.../modified_k_means_clustering.rs | 2 +-
src/tools/image_analysis/mosaic.rs | 6 +-
.../image_analysis/mosaic_with_feathering.rs | 10 +-
.../normalized_difference_index.rs | 2 +-
src/tools/image_analysis/olympic_filter.rs | 2 +-
src/tools/image_analysis/opening.rs | 2 +-
src/tools/image_analysis/pan_sharpening.rs | 2 +-
.../percentage_contrast_stretch.rs | 2 +-
src/tools/image_analysis/percentile_filter.rs | 2 +-
src/tools/image_analysis/prewitt_filter.rs | 2 +-
src/tools/image_analysis/range_filter.rs | 2 +-
src/tools/image_analysis/remove_spurs.rs | 2 +-
src/tools/image_analysis/resample.rs | 6 +-
src/tools/image_analysis/rgb_to_ihs.rs | 2 +-
src/tools/image_analysis/roberts_filter.rs | 2 +-
src/tools/image_analysis/scharr_filter.rs | 2 +-
.../sigmoidal_contrast_stretch.rs | 2 +-
src/tools/image_analysis/sobel_filter.rs | 2 +-
.../image_analysis/split_colour_composite.rs | 2 +-
.../image_analysis/stdev_contrast_stretch.rs | 2 +-
src/tools/image_analysis/stdev_filter.rs | 2 +-
src/tools/image_analysis/thicken_line.rs | 2 +-
src/tools/image_analysis/tophat.rs | 2 +-
src/tools/image_analysis/total_filter.rs | 2 +-
src/tools/image_analysis/unsharp_masking.rs | 2 +-
.../user_defined_weights_filter.rs | 2 +-
.../write_func_memory_insertion.rs | 2 +-
src/tools/lidar_analysis/ascii_to_las.rs | 2 +-
src/tools/lidar_analysis/block_maximum.rs | 2 +-
src/tools/lidar_analysis/block_minimum.rs | 2 +-
.../lidar_analysis/classify_buildings.rs | 500 ++
.../lidar_analysis/classify_overlap_points.rs | 2 +-
.../lidar_analysis/clip_lidar_to_polygon.rs | 2 +-
.../erase_polygon_from_lidar.rs | 2 +-
.../lidar_analysis/filter_lidar_classes.rs | 2 +-
.../filter_lidar_scan_angles.rs | 2 +-
.../find_flightline_edge_points.rs | 2 +-
.../lidar_analysis/flightline_overlap.rs | 2 +-
src/tools/lidar_analysis/las_to_ascii.rs | 2 +-
.../las_to_multipoint_shapefile.rs | 2 +-
src/tools/lidar_analysis/las_to_shapefile.rs | 2 +-
.../lidar_analysis/lidar_classify_subset.rs | 4 +-
src/tools/lidar_analysis/lidar_colourize.rs | 13 +-
.../lidar_construct_vector_tin.rs | 2 +-
.../lidar_analysis/lidar_elevation_slice.rs | 2 +-
.../lidar_ground_point_filter.rs | 2 +-
src/tools/lidar_analysis/lidar_hex_bin.rs | 2 +-
src/tools/lidar_analysis/lidar_hillshade.rs | 2 +-
src/tools/lidar_analysis/lidar_histogram.rs | 2 +-
.../lidar_analysis/lidar_idw_interpolation.rs | 4 +-
src/tools/lidar_analysis/lidar_info.rs | 6 +-
src/tools/lidar_analysis/lidar_join.rs | 2 +-
src/tools/lidar_analysis/lidar_kappa.rs | 2 +-
src/tools/lidar_analysis/lidar_nn_gridding.rs | 2 +-
src/tools/lidar_analysis/lidar_outliers.rs | 2 +-
.../lidar_analysis/lidar_point_density.rs | 2 +-
src/tools/lidar_analysis/lidar_point_stats.rs | 2 +-
...dar_radial_basis_function_interpolation.rs | 1023 +++
.../lidar_analysis/lidar_ransac_planes.rs | 2 +-
.../lidar_analysis/lidar_segmentation.rs | 2 +-
.../lidar_segmentation_based_filter.rs | 2 +-
src/tools/lidar_analysis/lidar_thin.rs | 2 +-
.../lidar_analysis/lidar_thin_high_density.rs | 2 +-
src/tools/lidar_analysis/lidar_tile.rs | 2 +-
.../lidar_analysis/lidar_tile_footprint.rs | 21 +-
.../lidar_analysis/lidar_tin_gridding.rs | 3 +-
.../lidar_analysis/lidar_tophat_transform.rs | 2 +-
src/tools/lidar_analysis/mod.rs | 4 +
src/tools/lidar_analysis/normal_vectors.rs | 2 +-
src/tools/lidar_analysis/remove_duplicates.rs | 2 +-
.../lidar_analysis/select_tiles_by_polygon.rs | 2 +-
src/tools/math_stat_analysis/abs.rs | 2 +-
src/tools/math_stat_analysis/add.rs | 2 +-
src/tools/math_stat_analysis/and.rs | 2 +-
src/tools/math_stat_analysis/anova.rs | 2 +-
src/tools/math_stat_analysis/arccos.rs | 2 +-
src/tools/math_stat_analysis/arcosh.rs | 2 +-
src/tools/math_stat_analysis/arcsin.rs | 2 +-
src/tools/math_stat_analysis/arctan.rs | 2 +-
src/tools/math_stat_analysis/arsinh.rs | 2 +-
src/tools/math_stat_analysis/artanh.rs | 2 +-
src/tools/math_stat_analysis/atan2.rs | 2 +-
.../attribute_correlation.rs | 2 +-
.../math_stat_analysis/attribute_histogram.rs | 2 +-
.../attribute_scattergram.rs | 2 +-
src/tools/math_stat_analysis/ceil.rs | 2 +-
src/tools/math_stat_analysis/cos.rs | 2 +-
src/tools/math_stat_analysis/cosh.rs | 2 +-
.../math_stat_analysis/crispness_index.rs | 2 +-
.../math_stat_analysis/cross_tabulation.rs | 2 +-
.../math_stat_analysis/cumulative_dist.rs | 2 +-
src/tools/math_stat_analysis/decrement.rs | 2 +-
src/tools/math_stat_analysis/divide.rs | 2 +-
src/tools/math_stat_analysis/equal_to.rs | 2 +-
src/tools/math_stat_analysis/exp.rs | 2 +-
src/tools/math_stat_analysis/exp2.rs | 2 +-
src/tools/math_stat_analysis/floor.rs | 2 +-
src/tools/math_stat_analysis/greater_than.rs | 2 +-
.../image_autocorrelation.rs | 2 +-
.../math_stat_analysis/image_correlation.rs | 2 +-
...mage_correlation_neighbourhood_analysis.rs | 803 ++
.../math_stat_analysis/image_regression.rs | 2 +-
src/tools/math_stat_analysis/increment.rs | 2 +-
src/tools/math_stat_analysis/inplace_add.rs | 2 +-
.../math_stat_analysis/inplace_divide.rs | 2 +-
.../math_stat_analysis/inplace_multiply.rs | 2 +-
.../math_stat_analysis/inplace_subtract.rs | 2 +-
.../math_stat_analysis/integer_division.rs | 2 +-
src/tools/math_stat_analysis/isnodata.rs | 2 +-
src/tools/math_stat_analysis/kappa_index.rs | 2 +-
.../math_stat_analysis/ks_normality_test.rs | 2 +-
src/tools/math_stat_analysis/less_than.rs | 2 +-
.../math_stat_analysis/list_unique_values.rs | 2 +-
src/tools/math_stat_analysis/ln.rs | 2 +-
src/tools/math_stat_analysis/log10.rs | 2 +-
src/tools/math_stat_analysis/log2.rs | 2 +-
src/tools/math_stat_analysis/max.rs | 2 +-
src/tools/math_stat_analysis/min.rs | 2 +-
src/tools/math_stat_analysis/mod.rs | 2 +
src/tools/math_stat_analysis/modulo.rs | 2 +-
src/tools/math_stat_analysis/multiply.rs | 2 +-
src/tools/math_stat_analysis/negate.rs | 2 +-
src/tools/math_stat_analysis/not.rs | 2 +-
src/tools/math_stat_analysis/not_equal_to.rs | 2 +-
src/tools/math_stat_analysis/or.rs | 2 +-
.../paired_sample_t_test.rs | 2 +-
src/tools/math_stat_analysis/power.rs | 2 +-
.../principal_component_analysis.rs | 2 +-
src/tools/math_stat_analysis/quantiles.rs | 2 +-
src/tools/math_stat_analysis/random_field.rs | 2 +-
src/tools/math_stat_analysis/random_sample.rs | 2 +-
.../math_stat_analysis/raster_histogram.rs | 2 +-
.../raster_summary_stats.rs | 2 +-
src/tools/math_stat_analysis/reciprocal.rs | 2 +-
.../math_stat_analysis/rescale_value_range.rs | 2 +-
.../root_mean_square_error.rs | 2 +-
src/tools/math_stat_analysis/round.rs | 2 +-
src/tools/math_stat_analysis/sin.rs | 2 +-
src/tools/math_stat_analysis/sinh.rs | 2 +-
src/tools/math_stat_analysis/sqrt.rs | 2 +-
src/tools/math_stat_analysis/square.rs | 2 +-
src/tools/math_stat_analysis/subtract.rs | 2 +-
src/tools/math_stat_analysis/tan.rs | 2 +-
src/tools/math_stat_analysis/tanh.rs | 2 +-
src/tools/math_stat_analysis/to_degrees.rs | 2 +-
src/tools/math_stat_analysis/to_radians.rs | 2 +-
src/tools/math_stat_analysis/trend_surface.rs | 2 +-
.../trend_surface_vector_points.rs | 2 +-
src/tools/math_stat_analysis/truncate.rs | 2 +-
src/tools/math_stat_analysis/turning_bands.rs | 2 +-
.../math_stat_analysis/two_sample_ks_test.rs | 2 +-
.../wilcoxon_signed_rank_test.rs | 8 +-
src/tools/math_stat_analysis/xor.rs | 2 +-
.../math_stat_analysis/zonal_statistics.rs | 2 +-
src/tools/math_stat_analysis/zscores.rs | 2 +-
src/tools/mod.rs | 14 +
.../stream_network_analysis/dist_to_outlet.rs | 2 +-
.../extract_streams.rs | 2 +-
.../extract_valleys.rs | 2 +-
.../farthest_channel_head.rs | 2 +-
.../stream_network_analysis/find_main_stem.rs | 2 +-
.../stream_network_analysis/hack_order.rs | 2 +-
.../stream_network_analysis/horton_order.rs | 2 +-
.../stream_network_analysis/long_profile.rs | 2 +-
.../long_profile_from_points.rs | 2 +-
.../raster_streams_to_vector.rs | 2 +-
.../rasterize_streams.rs | 2 +-
.../remove_short_streams.rs | 2 +-
.../shreve_magnitude.rs | 2 +-
.../stream_network_analysis/strahler_order.rs | 2 +-
.../stream_link_class.rs | 2 +-
.../stream_network_analysis/stream_link_id.rs | 2 +-
.../stream_link_length.rs | 2 +-
.../stream_link_slope.rs | 2 +-
.../stream_slope_continuous.rs | 2 +-
.../topological_stream_order.rs | 2 +-
.../total_length_channels.rs | 2 +-
.../stream_network_analysis/tributary_id.rs | 2 +-
.../vector_stream_network_analysis.rs | 2 +-
src/tools/terrain_analysis/aspect.rs | 2 +-
...average_normal_vector_angular_deviation.rs | 2 +-
.../circular_variance_of_aspect.rs | 2 +-
.../terrain_analysis/dev_from_mean_elev.rs | 2 +-
.../terrain_analysis/diff_from_mean_elev.rs | 2 +-
.../terrain_analysis/directional_relief.rs | 2 +-
src/tools/terrain_analysis/downslope_index.rs | 2 +-
.../drainage_preserving_smoothing.rs | 2 +-
src/tools/terrain_analysis/edge_density.rs | 2 +-
src/tools/terrain_analysis/elev_above_pit.rs | 2 +-
src/tools/terrain_analysis/elev_percentile.rs | 2 +-
.../elev_relative_to_min_max.rs | 2 +-
.../elev_relative_to_watershed_min_max.rs | 2 +-
.../feature_preserving_smoothing.rs | 2 +-
src/tools/terrain_analysis/fetch_analysis.rs | 2 +-
.../terrain_analysis/fill_missing_data.rs | 2 +-
src/tools/terrain_analysis/find_ridges.rs | 2 +-
src/tools/terrain_analysis/geomorphons.rs | 2 +-
src/tools/terrain_analysis/hillshade.rs | 2 +-
src/tools/terrain_analysis/horizon_angle.rs | 2 +-
.../terrain_analysis/hypsometric_analysis.rs | 2 +-
.../terrain_analysis/max_anisotropy_dev.rs | 2 +-
.../max_anisotropy_dev_signature.rs | 2 +-
.../terrain_analysis/max_branch_length.rs | 2 +-
.../terrain_analysis/max_diff_from_mean.rs | 2 +-
.../max_downslope_elev_change.rs | 2 +-
.../max_elev_dev_signature.rs | 2 +-
.../terrain_analysis/max_elev_deviation.rs | 2 +-
.../min_downslope_elev_change.rs | 2 +-
.../terrain_analysis/multiscale_roughness.rs | 2 +-
.../multiscale_roughness_signature.rs | 2 +-
.../multiscale_std_dev_normals.rs | 2 +-
.../multiscale_std_dev_normals_signature.rs | 2 +-
.../multiscale_topographic_position_image.rs | 2 +-
.../num_downslope_neighbours.rs | 2 +-
.../num_upslope_neighbours.rs | 2 +-
.../pennock_landform_class.rs | 2 +-
.../terrain_analysis/percent_elev_range.rs | 2 +-
src/tools/terrain_analysis/plan_curvature.rs | 2 +-
src/tools/terrain_analysis/prof_curvature.rs | 2 +-
src/tools/terrain_analysis/profile.rs | 2 +-
src/tools/terrain_analysis/relative_aspect.rs | 2 +-
.../relative_stream_power_index.rs | 2 +-
.../relative_topographic_position.rs | 2 +-
.../remove_off_terrain_objects.rs | 2 +-
.../terrain_analysis/ruggedness_index.rs | 2 +-
.../sediment_transport_index.rs | 4 +-
src/tools/terrain_analysis/slope.rs | 2 +-
.../terrain_analysis/slope_vs_elev_plot.rs | 4 +-
.../spherical_std_dev_of_normals.rs | 2 +-
.../standard_deviation_of_slope.rs | 2 +-
.../terrain_analysis/surface_area_ratio.rs | 2 +-
src/tools/terrain_analysis/tan_curvature.rs | 2 +-
src/tools/terrain_analysis/total_curvature.rs | 2 +-
src/tools/terrain_analysis/viewshed.rs | 2 +-
.../terrain_analysis/visibility_index.rs | 2 +-
src/tools/terrain_analysis/wetness_index.rs | 2 +-
src/vector/shapefile/mod.rs | 8 +-
tempCodeRunnerFile.py | 7321 -----------------
wb_runner.py | 639 +-
wb_runner_new.py | 1321 ---
whitebox_example.py | 143 -
whitebox_tools.py | 131 +-
466 files changed, 8983 insertions(+), 10697 deletions(-)
delete mode 100644 build.py
delete mode 100644 lib_test.py
create mode 100644 src/structures/radial_basis_function.rs
create mode 100644 src/tools/gis_analysis/natural_neighbour_interpolation.rs
create mode 100644 src/tools/gis_analysis/raster_perimeter.rs
create mode 100644 src/tools/hydro_analysis/breach_depressions_least_cost.rs
create mode 100644 src/tools/hydro_analysis/fill_depressions_wang_and_lui.rs
create mode 100644 src/tools/hydro_analysis/upslope_depression_storage.rs
create mode 100644 src/tools/lidar_analysis/classify_buildings.rs
create mode 100644 src/tools/lidar_analysis/lidar_radial_basis_function_interpolation.rs
create mode 100644 src/tools/math_stat_analysis/image_correlation_neighbourhood_analysis.rs
delete mode 100644 tempCodeRunnerFile.py
delete mode 100644 wb_runner_new.py
delete mode 100644 whitebox_example.py
diff --git a/.DS_Store b/.DS_Store
index 41ea59f38a404bd074e058ed0e10b5efc74fc9aa..9b5f7c955d59016f4a2273ae397a0a2d055ea31e 100644
GIT binary patch
delta 24
fcmZn(XbIR5C&X@OtfOFJVKn)isO;t@p(arPV3-Gw
delta 24
fcmZn(XbIR5C&X@GqN8ABW;*$tsO;t@p(arPV21~e
diff --git a/Cargo.lock b/Cargo.lock
index 3194f678f..b35856703 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -154,7 +154,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "kdtree"
-version = "0.5.1"
+version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -569,11 +569,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "whitebox_tools"
-version = "1.0.2"
+version = "1.0.3"
dependencies = [
"byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
- "kdtree 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "kdtree 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libflate 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)",
"lzw 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
"nalgebra 0.18.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -640,7 +640,7 @@ dependencies = [
"checksum generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec"
"checksum getrandom 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "e65cce4e5084b14874c4e7097f38cab54f47ee554f9194673456ea379dcc4c55"
"checksum itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "1306f3464951f30e30d12373d31c79fbd52d236e5e896fd92f96ec7babbbe60b"
-"checksum kdtree 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b63d659081717fe428fbaf9549559083956450194e57e8d28498a0dfa31b3b04"
+"checksum kdtree 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "80ee359328fc9087e9e3fc0a4567c4dd27ec69a127d6a70e8d9dd22845b8b1a2"
"checksum lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bc5729f27f159ddd61f4df6228e827e86643d4d3e7c32183cb30a1c08f604a14"
"checksum libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)" = "6281b86796ba5e4366000be6e9e18bf35580adf9e63fbe2294aadb587613a319"
"checksum libflate 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)" = "7346a83e8a2c3958d44d24225d905385dc31fc16e89dffb356c457b278914d20"
diff --git a/Cargo.toml b/Cargo.toml
index ac03254b4..65701bd41 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,7 +1,7 @@
[package]
name = "whitebox_tools"
-version = "1.0.2"
+version = "1.0.3"
authors = ["John Lindsay "]
description = "A library for analyzing geospatial data."
keywords = ["geospatial", "GIS", "remote sensing", "geomatics", "image processing", "lidar", "spatial analysis"]
@@ -13,7 +13,7 @@ edition = "2018"
[dependencies]
byteorder = "^1.3.1"
chrono = "0.4.6"
-kdtree = "0.5.1"
+kdtree = "0.6.0"
libflate = "0.1.18"
lzw = "0.10.0"
nalgebra = "0.18.0"
diff --git a/build.py b/build.py
deleted file mode 100644
index 7d2fadfed..000000000
--- a/build.py
+++ /dev/null
@@ -1,70 +0,0 @@
-#!/usr/bin/env python3
-''' this module is used to build whitebox-tools.
-'''
-import os
-import sys
-from subprocess import call
-
-
-def main():
- ''' main function
- '''
- try:
- update_cargo = False
- clean_code = False
- doc_code = False
- build_code = True
- mode = 'release' # 'check', 'debug', or 'release'
-
- # Change the current directory
- dir_path = os.path.dirname(os.path.realpath(__file__))
- os.chdir(dir_path)
-
- if update_cargo:
- # Update #
- retcode = call(['cargo', 'update'], shell=False)
- if retcode < 0:
- print("Child was terminated by signal", -
- retcode, file=sys.stderr)
- else:
- print("Update successful", file=sys.stderr)
-
- if clean_code:
- # Clean #
- retcode = call(['cargo', 'clean'], shell=False)
- if retcode < 0:
- print("Child was terminated by signal", -
- retcode, file=sys.stderr)
- else:
- print("Clean successful", file=sys.stderr)
-
- if doc_code:
- # Clean #
- retcode = call(['cargo', 'doc'], shell=False)
- if retcode < 0:
- print("Child was terminated by signal", -
- retcode, file=sys.stderr)
- else:
- print("Clean successful", file=sys.stderr)
-
- if build_code:
- # Build #
- if mode == 'release':
- retcode = call(['env', 'RUSTFLAGS=-C target-cpu=native', 'CARGO_INCREMENTAL=1',
- 'cargo', 'build', '--release'], shell=False)
- elif mode == 'check':
- retcode = call(['cargo', 'check'], shell=False)
- else:
- retcode = call(['cargo', 'build'], shell=False)
-
- if retcode < 0:
- print("Child was terminated by signal", -
- retcode, file=sys.stderr)
- else:
- print("Build executed successfully", file=sys.stderr)
-
- except OSError as err:
- print("Execution failed:", err, file=sys.stderr)
-
-
-main()
diff --git a/lib_test.py b/lib_test.py
deleted file mode 100644
index ab58f38e6..000000000
--- a/lib_test.py
+++ /dev/null
@@ -1,45 +0,0 @@
-#!/usr/bin/env python3
-''' This script is intended to experiment with the use of a whitebox_tools shared library (DLL).
-It is experimental and is not intended for widespread use.
-'''
-import os
-from sys import platform
-from ctypes import cdll, c_int, c_char_p, POINTER, c_size_t
-
-
-def call_tool(name, args):
- # Change the current directory
- dir_path = os.path.dirname(os.path.realpath(__file__))
- os.chdir(dir_path)
-
- if platform == 'darwin':
- prefix = 'lib'
- ext = 'dylib'
- elif platform == 'win32':
- prefix = ''
- ext = 'dll'
- else:
- prefix = 'lib'
- ext = 'so'
-
- wb_tools = cdll.LoadLibrary(
- 'target/release/{}whitebox_tools.{}'.format(prefix, ext))
-
- wb_tools.run_tool.restype = c_int
- wb_tools.run_tool.argtypes = [
- c_char_p, POINTER(c_char_p), c_size_t]
-
- wb_tools.print_tool.argtypes = [
- c_char_p]
-
- args_list = (c_char_p * len(args))(*args)
- ret = wb_tools.run_tool(name, args_list, len(args_list))
- print "Return value:", ret
-
-
-TOOL_NAME = "slope"
-TOOL_ARGS = ["--wd=\"/Users/johnlindsay/Documents/data/GarveyGlenWatershed/\"",
- "--input=\"DEM final.dep\"",
- "--output=\"tmp13.dep\"",
- "-v"]
-call_tool(TOOL_NAME, TOOL_ARGS)
diff --git a/readme.txt b/readme.txt
index 603633057..d21ecad3a 100644
--- a/readme.txt
+++ b/readme.txt
@@ -56,6 +56,36 @@ for more details.
* Release Notes: *
******************
+Version 1.0.3 (09-12-2019)
+- Added the BreachDepressionsLeastCost tool, which performs a modified form of the Lindsay
+ and Dhun (2015) impact minimizing breaching algorithm. This modified algorithm is very
+ efficient and can provide an excellent method for creating depressionless DEMs from large
+ DEMs, including those derived from LiDAR. It is particularly well suited to breaching
+ through road embankments, approximately the pathway of culverts.
+- The FillDepressions tool algorithm has been completely re-developed. The new algorithm is
+ significantly faster than the previous method, which was based on the Wang and Lui method.
+ For legacy reasons, the previous tool has been retained and renamed FillDepressonsWangAndLui.
+ Notice that this new method also incorporates significantly improved flat area correction
+ that maintains general flowpaths of filled areas.
+- The Sink and DepthInSink tools have been updated to use the new depression filling algorithm.
+- Added the ClassifyBuildingsInLidar tool to reclassify LiDAR points within a LAS file
+ to the building class value (6) that are located within one or more building footprint
+ contained in an input polygon vector file.
+- Added the NaturalNeighbourInterpolation tool for performing Sibson's (1981) interpolation
+ method on input point data.
+- Added the UpslopeDepressionStorage tool to estimate the average upslope depression
+ storage capacity (DSC).
+- Added the LidarRfbInterpolation tool for performing a radial basis function interpolation
+ of LiDAR data sets.
+- The WhiteboxTools Runner user interface has been significantly improved (many thanks to
+ Rachel Broders for these contributions).
+- Fixed a bug in which the photometric interpretation was not being set by certain raster
+ decoders, including the SAGA encoder. This was causing an error when outputting GeoTIFF
+ files.
+- Updated the ConstructVectorTIN and TINGridding tools to include a maximum triangle edge
+ length to help avoid the creation of spurious long and narrow triangles in convex regions
+ along the data boundaries.
+
Version 1.0.2 (01-11-2019)
- Added the BurnStreamsAtRoads tool.
- Added a two-sample K-S test (TwoSampleKsTest) for comparing the distributions of two rasters.
diff --git a/src/algorithms/delaunay_triangulation.rs b/src/algorithms/delaunay_triangulation.rs
index 7d0296255..9b27fbee9 100644
--- a/src/algorithms/delaunay_triangulation.rs
+++ b/src/algorithms/delaunay_triangulation.rs
@@ -45,6 +45,7 @@ println!("{:?}", result.triangles); // [0, 2, 1, 0, 3, 2]
use crate::structures::Point2D;
use std::f64;
+use std::collections::HashSet;
/// Represents the area outside of the triangulation.
/// Halfedges on the convex hull (which don't have an adjacent halfedge)
@@ -165,6 +166,60 @@ impl Triangulation {
result
}
+ pub fn natural_neighbours_from_incoming_edge(&self, start: usize) -> Vec {
+ let mut result = vec![];
+ //result.push(self.triangles[self.next_halfedge(start)]);
+ let mut incoming = start;
+ let mut outgoing: usize;
+ loop {
+ result.push(self.triangles[incoming]);
+ outgoing = self.next_halfedge(incoming);
+ incoming = self.halfedges[outgoing];
+ if incoming == EMPTY {
+ break;
+ } else if incoming == start {
+ break;
+ }
+ }
+ result
+ }
+
+ pub fn natural_neighbours_2nd_order(&self, start: usize) -> Vec {
+ let mut set = HashSet::new();
+ let mut edges = vec![];
+ // result.push(self.triangles[self.next_halfedge(start)]);
+ // set.insert(self.triangles[self.next_halfedge(start)]);
+ let mut incoming = start;
+ let mut outgoing: usize;
+ loop {
+ set.insert(self.triangles[incoming]);
+ outgoing = self.next_halfedge(incoming);
+ incoming = self.halfedges[outgoing];
+ edges.push(outgoing);
+ if incoming == EMPTY {
+ break;
+ } else if incoming == start {
+ break;
+ }
+ }
+
+ for start in edges {
+ incoming = start;
+ loop {
+ set.insert(self.triangles[incoming]);
+ outgoing = self.next_halfedge(incoming);
+ incoming = self.halfedges[outgoing];
+ if incoming == EMPTY {
+ break;
+ } else if incoming == start {
+ break;
+ }
+ }
+ }
+
+ set.into_iter().map(|i| i).collect()
+ }
+
/// Returns the indices of the adjacent triangles to a triangle.
pub fn triangles_adjacent_to_triangle(&self, triangle: usize) -> Vec {
let mut adjacent_triangles: Vec = vec![];
diff --git a/src/raster/arcascii_raster.rs b/src/raster/arcascii_raster.rs
index 129ea9a67..59beb43a1 100644
--- a/src/raster/arcascii_raster.rs
+++ b/src/raster/arcascii_raster.rs
@@ -112,6 +112,8 @@ pub fn read_arcascii(
yllcenter - (0.5 * configs.resolution_y) + (configs.rows as f64) * configs.resolution_y;
}
+ configs.photometric_interp = PhotometricInterpretation::Continuous;
+
Ok(())
}
diff --git a/src/raster/arcbinary_raster.rs b/src/raster/arcbinary_raster.rs
index 156963eea..53262b670 100644
--- a/src/raster/arcbinary_raster.rs
+++ b/src/raster/arcbinary_raster.rs
@@ -83,6 +83,8 @@ pub fn read_arcbinary(
}
}
+ configs.photometric_interp = PhotometricInterpretation::Continuous;
+
configs.data_type = DataType::F32;
// set the North, East, South, and West coodinates
diff --git a/src/raster/geotiff/geokeys.rs b/src/raster/geotiff/geokeys.rs
index 15913b58c..002ee674d 100644
--- a/src/raster/geotiff/geokeys.rs
+++ b/src/raster/geotiff/geokeys.rs
@@ -1,5 +1,6 @@
use super::Ifd;
use crate::utils::{ByteOrderReader, Endianness};
+use crate::spatial_ref_system;
use std::collections::HashMap;
use std::fmt;
use std::mem::transmute;
@@ -205,8 +206,20 @@ impl GeoKeys {
if keyword_map.contains_key(&key_code) {
match keyword_map.get(&key_code) {
Some(hm) => match hm.get(&value_offset) {
- Some(v) => value = format!("{} ({})", v.to_string(), value_offset), //v.to_string(),
- None => value = format!("Unrecognized value ({})", value_offset),
+ Some(v) => {
+ value = if key_code == 3072 || key_code == 2048 {
+ format!("{} ({})", v.to_string(), spatial_ref_system::esri_wkt_from_epsg(value_offset))
+ } else {
+ format!("{} ({})", v.to_string(), value_offset)
+ };
+ },
+ None => {
+ value = if key_code == 3072 || key_code == 2048 {
+ spatial_ref_system::esri_wkt_from_epsg(value_offset)
+ } else {
+ format!("Unrecognized value ({})", value_offset)
+ };
+ }
},
None => value = format!("Unrecognized value ({})", key_code),
}
@@ -2651,6 +2664,7 @@ pub fn get_keyword_map() -> HashMap> {
kw.insert(3076u16, proj_linear_units_map);
let vertical_cs_type_map = hashmap![
+ 1127=>"Canadian Geodetic Vertical Datum of 2013 (CGVD2013)",
5001=>"VertCS_Airy_1830_ellipsoid",
5002=>"VertCS_Airy_Modified_1849_ellipsoid",
5003=>"VertCS_ANS_ellipsoid",
diff --git a/src/raster/geotiff/tiff_consts.rs b/src/raster/geotiff/tiff_consts.rs
index cf8be7234..9acdd3381 100644
--- a/src/raster/geotiff/tiff_consts.rs
+++ b/src/raster/geotiff/tiff_consts.rs
@@ -1,162 +1,3 @@
-// #[repr(u16)]
-// pub enum CompressType {
-// COMPRESS_NONE = 1,
-// COMPRESS_CCITT = 2,
-// COMPRESS_G3 = 3, // Group 3 Fax.
-// COMPRESS_G4 = 4, // Group 4 Fax.
-// COMPRESS_LZW = 5,
-// COMPRESS_JPEGOLD = 6, // Superseded by cJPEG.
-// COMPRESS_JPEG = 7,
-// COMPRESS_DEFLATE = 8, // zlib compression.
-// COMPRESS_PACKBITS = 32773,
-// COMPRESS_DEFLATEOLD = 32946, // Superseded by cDeflate.
-// }
-
-// #[repr(u16)]
-// pub enum TagType {
-// DT_BYTE = 1,
-// DT_ASCII = 2,
-// DT_SHORT = 3,
-// DT_LONG = 4,
-// DT_RATIONAL = 5,
-// DT_SBYTE = 6,
-// DT_UNDEFINED = 7,
-// DT_SSHORT = 8,
-// DT_SLONG = 9,
-// DT_SRATIONAL = 10,
-// DT_FLOAT = 11,
-// DT_DOUBLE = 12,
-// }
-
-// const DT_BYTE: u16 = 1;
-// const DT_ASCII: u16 = 2;
-// const DT_SHORT: u16 = 3;
-// const DT_LONG: u16 = 4;
-// const DT_RATIONAL: u16 = 5;
-// const DT_SBYTE: u16 = 6;
-// const DT_UNDEFINED: u16 = 7;
-// const DT_SSHORT: u16 = 8;
-// const DT_SLONG: u16 = 9;
-// const DT_SRATIONAL: u16 = 10;
-// const DT_FLOAT: u16 = 11;
-// const DT_DOUBLE: u16 = 12;
-
-// #[repr(u16)]
-// pub enum PhotometricInterpretation {
-// PI_WHITEISZERO = 0,
-// PI_BLACKISZERO = 1,
-// PI_RGB = 2,
-// PI_PALETTED = 3,
-// // const PI_TRANSMASK: u16 = 4; // transparency mask
-// // const PI_CMYK: u16 = 5;
-// // const PI_YCBCR: u16 = 6;
-// // const PI_CIELAB: u16 = 8;
-// }
-
-// // Tags (see p. 28-41 of the spec).
-// #[repr(u16)]
-// pub enum TiffTags {
-// TAG_NEWSUBFILETYPE = 254u16,
-// TAG_IMAGEWIDTH = 256u16,
-// TAG_IMAGELENGTH = 257u16,
-// TAG_BITSPERSAMPLE = 258u16,
-// TAG_COMPRESSION = 259u16,
-// TAG_PHOTOMETRICINTERPRETATION = 262u16,
-// TAG_FILLORDER = 266u16,
-// TAG_DOCUMENTNAME = 269u16,
-// TAG_PLANARCONFIGURATION = 284u16,
-
-// TAG_STRIPOFFSETS = 273u16,
-// TAG_ORIENTATION = 274u16,
-// TAG_SAMPLESPERPIXEL = 277u16,
-// TAG_ROWSPERSTRIP = 278u16,
-// TAG_STRIPBYTECOUNTS = 279u16,
-
-// TAG_TILEWIDTH = 322u16,
-// TAG_TILELENGTH = 323u16,
-// TAG_TILEOFFSETS = 324u16,
-// TAG_TILEBYTECOUNTS = 325u16,
-
-// TAG_XRESOLUTION = 282u16,
-// TAG_YRESOLUTION = 283u16,
-// TAG_RESOLUTIONUNIT = 296u16,
-
-// TAG_SOFTWARE = 305u16,
-// TAG_PREDICTOR = 317u16,
-// TAG_COLORMAP = 320u16,
-// TAG_EXTRASAMPLES = 338u16,
-// TAG_SAMPLEFORMAT = 339u16,
-
-// TAG_GDAL_METADATA = 42112u16,
-// TAG_GDAL_NODATA = 42113u16,
-
-// TAG_MODELPIXELSCALETAG = 33550u16,
-// TAG_MODELTRANSFORMATIONTAG = 34264u16,
-// TAG_MODELTIEPOINTTAG = 33922u16,
-// TAG_GEOKEYDIRECTORYTAG = 34735u16,
-// TAG_GEODOUBLEPARAMSTAG = 34736u16,
-// TAG_GEOASCIIPARAMSTAG = 34737u16,
-// TAG_INTERGRAPHMATRIXTAG = 33920u16,
-
-// TAG_GTMODELTYPEGEOKEY = 1024u16,
-// TAG_GTRASTERTYPEGEOKEY = 1025u16,
-// TAG_GTCITATIONGEOKEY = 1026u16,
-// TAG_GEOGRAPHICTYPEGEOKEY = 2048u16,
-// TAG_GEOGCITATIONGEOKEY = 2049u16,
-// TAG_GEOGGEODETICDATUMGEOKEY = 2050u16,
-// TAG_GEOGPRIMEMERIDIANGEOKEY = 2051u16,
-// TAG_GEOGLINEARUNITSGEOKEY = 2052u16,
-// TAG_GEOGLINEARUNITSIZEGEOKEY = 2053u16,
-// TAG_GEOGANGULARUNITSGEOKEY = 2054u16,
-// TAG_GEOGANGULARUNITSIZEGEOKEY = 2055u16,
-// TAG_GEOGELLIPSOIDGEOKEY = 2056u16,
-// TAG_GEOGSEMIMAJORAXISGEOKEY = 2057u16,
-// TAG_GEOGSEMIMINORAXISGEOKEY = 2058u16,
-// TAG_GEOGINVFLATTENINGGEOKEY = 2059u16,
-// TAG_GEOGAZIMUTHUNITSGEOKEY = 2060u16,
-// TAG_GEOGPRIMEMERIDIANLONGGEOKEY = 2061u16,
-// TAG_PROJECTEDCSTYPEGEOKEY = 3072u16,
-// TAG_PCSCITATIONGEOKEY = 3073u16,
-// TAG_PROJECTIONGEOKEY = 3074u16,
-// TAG_PROJCOORDTRANSGEOKEY = 3075u16,
-// TAG_PROJLINEARUNITSGEOKEY = 3076u16,
-// TAG_PROJLINEARUNITSIZEGEOKEY = 3077u16,
-// TAG_PROJSTDPARALLEL1GEOKEY = 3078u16,
-// TAG_PROJSTDPARALLEL2GEOKEY = 3079u16,
-// TAG_PROJNATORIGINLONGGEOKEY = 3080u16,
-// TAG_PROJNATORIGINLATGEOKEY = 3081u16,
-// TAG_PROJFALSEEASTINGGEOKEY = 3082u16,
-// TAG_PROJFALSENORTHINGGEOKEY = 3083u16,
-// TAG_PROJFALSEORIGINLONGGEOKEY = 3084u16,
-// TAG_PROJFALSEORIGINLATGEOKEY = 3085u16,
-// TAG_PROJFALSEORIGINEASTINGGEOKEY = 3086u16,
-// TAG_PROJFALSEORIGINNORTHINGGEOKEY = 3087u16,
-// TAG_PROJCENTERLONGGEOKEY = 3088u16,
-// TAG_PROJCENTERLATGEOKEY = 3089u16,
-// TAG_PROJCENTEREASTINGGEOKEY = 3090u16,
-// TAG_PROJCENTERNORTHINGGEOKEY = 3091u16,
-// TAG_PROJSCALEATNATORIGINGEOKEY = 3092u16,
-// TAG_PROJSCALEATCENTERGEOKEY = 3093u16,
-// TAG_PROJAZIMUTHANGLEGEOKEY = 3094u16,
-// TAG_PROJSTRAIGHTVERTPOLELONGGEOKEY = 3095u16,
-// TAG_VERTICALCSTYPEGEOKEY = 4096u16,
-// TAG_VERTICALCITATIONGEOKEY = 4097u16,
-// TAG_VERTICALDATUMGEOKEY = 4098u16,
-// TAG_VERTICALUNITSGEOKEY = 4099u16,
-
-// TAG_PHOTOSHOP = 34377u16,
-// }
-
-// pub enum ImageMode {
-// Bilevel = 1,
-// Paletted = 2,
-// Gray = 3,
-// GrayInvert = 4,
-// RGB = 5,
-// RGBA = 6,
-// NRGBA = 7,
-// }
-
// Image Modes
pub const IM_BILEVEL: u16 = 1u16;
pub const IM_PALETTED: u16 = 2u16;
@@ -248,6 +89,7 @@ pub const TAG_MODELTIEPOINTTAG: u16 = 33922u16;
pub const TAG_GEOKEYDIRECTORYTAG: u16 = 34735u16;
pub const TAG_GEODOUBLEPARAMSTAG: u16 = 34736u16;
pub const TAG_GEOASCIIPARAMSTAG: u16 = 34737u16;
+pub const TAG_INTERGRRAPH_PACKET_DATA: u16 = 33918u16;
pub const TAG_INTERGRAPHMATRIXTAG: u16 = 33920u16;
pub const TAG_GTMODELTYPEGEOKEY: u16 = 1024u16;
diff --git a/src/raster/grass_raster.rs b/src/raster/grass_raster.rs
index 88ff2c9f3..679dda58a 100644
--- a/src/raster/grass_raster.rs
+++ b/src/raster/grass_raster.rs
@@ -98,6 +98,8 @@ pub fn read_grass_raster(
}
}
+ configs.photometric_interp = PhotometricInterpretation::Continuous;
+
Ok(())
}
diff --git a/src/raster/idrisi_raster.rs b/src/raster/idrisi_raster.rs
index d9c8d621f..221189538 100644
--- a/src/raster/idrisi_raster.rs
+++ b/src/raster/idrisi_raster.rs
@@ -150,9 +150,9 @@ pub fn read_idrisi(
f.read(&mut buffer)?;
// read the file's bytes into a buffer
- //try!(f.read_to_end(&mut buffer));
+ //f.read_to_end(&mut buffer)?;
- //try!(br.fill_buf().unwrap()(&mut buffer));
+ //br.fill_buf().unwrap()(&mut buffer)?;
let mut offset: usize;
match configs.data_type {
@@ -370,7 +370,7 @@ pub fn write_idrisi<'a>(r: &'a mut Raster) -> Result<(), Error> {
));
// for i in 0..num_cells {
// u24_bytes = unsafe { mem::transmute(r.data[i] as u32) };
- // try!(writer.write(&u16_bytes));
+ // writer.write(&u16_bytes)?;
// }
}
DataType::I16 => {
diff --git a/src/raster/mod.rs b/src/raster/mod.rs
index a497c87cc..f63a42d0f 100644
--- a/src/raster/mod.rs
+++ b/src/raster/mod.rs
@@ -1291,7 +1291,7 @@ fn get_raster_type_from_file(file_name: String, file_mode: String) -> RasterType
|| l.contains("xllcenter")
|| l.contains("yllcenter")
{
- return RasterType::ArcAscii;;
+ return RasterType::ArcAscii;
}
if line_count > 7 {
break;
diff --git a/src/raster/saga_raster.rs b/src/raster/saga_raster.rs
index cc120ea43..be9fc1abd 100644
--- a/src/raster/saga_raster.rs
+++ b/src/raster/saga_raster.rs
@@ -68,6 +68,7 @@ pub fn read_saga(
))
}
}
+ configs.photometric_interp = PhotometricInterpretation::Continuous;
} else if vec[0].to_lowercase().contains("byteorder_big") {
if vec[1].replace("=", "").trim().to_lowercase().contains("f")
|| vec[1]
@@ -143,8 +144,7 @@ pub fn read_saga(
configs.north = configs.south + configs.resolution_y * configs.rows as f64;
configs.east = configs.west + configs.resolution_x * configs.columns as f64;
- if z_factor < 0.0 && (configs.data_type == DataType::F32 || configs.data_type == DataType::F64)
- {
+ if z_factor < 0.0 && (configs.data_type == DataType::F32 || configs.data_type == DataType::F64) {
configs.data_type = DataType::F32;
}
@@ -367,6 +367,22 @@ pub fn read_saga(
}
}
+ ///////////////////////////////////////////
+ // Read the projection file if it exists //
+ ///////////////////////////////////////////
+ let prj_file = Path::new(&file_name).with_extension("prj").into_os_string().into_string().unwrap();
+ match File::open(prj_file) {
+ Ok(f) => {
+ configs.projection = String::new();
+ let f = BufReader::new(f);
+ for line in f.lines() {
+ let line_unwrapped = line.unwrap();
+ configs.projection.push_str(&format!("{}\n", line_unwrapped));
+ }
+ }
+ Err(_) => println!("Warning: Projection file not located."),
+ }
+
Ok(())
}
@@ -568,5 +584,16 @@ pub fn write_saga<'a>(r: &'a mut Raster) -> Result<(), Error> {
let _ = writer.flush();
+ ///////////////////////////////
+ // Write the projection file //
+ ///////////////////////////////
+
+ if !r.configs.projection.is_empty() {
+ let prj_file = Path::new(&r.file_name).with_extension("prj").into_os_string().into_string().unwrap();
+ let f = File::create(&prj_file)?;
+ let mut writer = BufWriter::new(f);
+ writer.write_all(r.configs.projection.as_bytes())?;
+ }
+
Ok(())
}
diff --git a/src/raster/surfer7_raster.rs b/src/raster/surfer7_raster.rs
index b266bc5d0..29c9c0c54 100644
--- a/src/raster/surfer7_raster.rs
+++ b/src/raster/surfer7_raster.rs
@@ -303,6 +303,8 @@ pub fn read_surfer7(
}
}
+ configs.photometric_interp = PhotometricInterpretation::Continuous;
+
Ok(())
}
diff --git a/src/raster/surfer_ascii_raster.rs b/src/raster/surfer_ascii_raster.rs
index e3332118f..fde21e0b8 100644
--- a/src/raster/surfer_ascii_raster.rs
+++ b/src/raster/surfer_ascii_raster.rs
@@ -109,6 +109,7 @@ pub fn read_surfer_ascii_raster(
line_num += 1;
}
+ configs.photometric_interp = PhotometricInterpretation::Continuous;
configs.resolution_x = (configs.east - configs.west) / configs.columns as f64;
configs.resolution_y = (configs.north - configs.south) / configs.rows as f64;
diff --git a/src/raster/whitebox_raster.rs b/src/raster/whitebox_raster.rs
index 5cd473436..b759de810 100644
--- a/src/raster/whitebox_raster.rs
+++ b/src/raster/whitebox_raster.rs
@@ -118,7 +118,7 @@ pub fn read_whitebox(
let data_file = Path::new(&file_name).with_extension("tas").into_os_string().into_string().unwrap();
let mut f = File::open(data_file.clone())?;
//let br = BufReader::new(f);
- // let metadata = try!(fs::metadata(data_file.clone()));
+ // let metadata = fs::metadata(data_file.clone())?;
// let file_size: usize = metadata.len() as usize;
// let mut buffer = vec![0; file_size];
@@ -426,12 +426,6 @@ pub fn write_whitebox<'a>(r: &'a mut Raster) -> Result<(), Error> {
}
}
DataType::I32 => {
- // if verbose {
- // println!("Warning: the I32 data type may not be supported on all versions of Whitebox GAT.");
- // }
- // for i in 0..num_cells {
- // writer.write_i32::(r.data[i] as i32)?;
- // }
for i in 0..num_cells {
writer.write_f32::(r.data[i] as f32)?;
}
diff --git a/src/structures/array2d.rs b/src/structures/array2d.rs
index ce9d41507..52e441c98 100644
--- a/src/structures/array2d.rs
+++ b/src/structures/array2d.rs
@@ -73,17 +73,17 @@ where
}
pub fn get_value(&self, row: isize, column: isize) -> T {
- // if row < 0 || column < 0 {
- // return self.nodata;
- // }
- // if row >= self.rows || column >= self.columns {
- // return self.nodata;
- // }
- // self.data[(row * self.columns + column) as usize]
- match self.data.get((row * self.columns + column) as usize) {
- Some(v) => return *v,
- None => return self.nodata(),
- };
+ if row < 0 || column < 0 {
+ return self.nodata;
+ }
+ if row >= self.rows || column >= self.columns {
+ return self.nodata;
+ }
+ self.data[(row * self.columns + column) as usize]
+ // match self.data.get((row * self.columns + column) as usize) {
+ // Some(v) => return *v,
+ // None => return self.nodata(),
+ // };
}
pub fn increment(&mut self, row: isize, column: isize, value: T) {
diff --git a/src/structures/mod.rs b/src/structures/mod.rs
index 57246f8af..5efaed00c 100644
--- a/src/structures/mod.rs
+++ b/src/structures/mod.rs
@@ -9,6 +9,7 @@ mod n_minimizer;
mod point2d;
mod polyline;
mod polynomial_regression_2d;
+mod radial_basis_function;
// exports identifiers from private sub-modules in the current module namespace
pub use self::array2d::Array2D;
@@ -22,4 +23,5 @@ pub use self::point2d::Direction;
pub use self::point2d::Point2D;
pub use self::polyline::MultiPolyline;
pub use self::polyline::Polyline;
-pub use self::polynomial_regression_2d::PolynomialRegression2D;
\ No newline at end of file
+pub use self::polynomial_regression_2d::PolynomialRegression2D;
+pub use self::radial_basis_function::{Basis, RadialBasisFunction};
\ No newline at end of file
diff --git a/src/structures/radial_basis_function.rs b/src/structures/radial_basis_function.rs
new file mode 100644
index 000000000..d3a0ec357
--- /dev/null
+++ b/src/structures/radial_basis_function.rs
@@ -0,0 +1,143 @@
+//! Based on rbf-interp, a library for multidimensional interpolation.
+//! by Raph Levien (raphlinus)
+//! https://github.com/linebender/rbf-interp/blob/master/src/lib.rs
+use nalgebra::{DMatrix, DVector, SVD};
+
+#[derive(Clone, Copy)]
+pub enum Basis {
+ ThinPlateSpine(f64),
+ PolyHarmonic(i32),
+ Gaussian(f64),
+ MultiQuadric(f64),
+ InverseMultiQuadric(f64),
+}
+
+impl Basis {
+ fn eval(&self, r: f64) -> f64 {
+ match self {
+ Basis::ThinPlateSpine(c) => (c * c + r * r) * (c * c + r * r).ln(), // from Surfer (see Geospatial Analysis 6th Ed., 2018)
+ Basis::PolyHarmonic(n) if n % 2 == 0 => {
+ // Somewhat arbitrary but don't expect tiny nonzero values.
+ if r < 1e-12 {
+ 0.0
+ } else {
+ r.powi(*n) * r.ln()
+ }
+ }
+ Basis::PolyHarmonic(n) => r.powi(*n),
+ // Note: it might be slightly more efficient to pre-recip c, but
+ // let's keep code clean for now.
+ Basis::Gaussian(c) => (-(r / c).powi(2)).exp(),
+ Basis::MultiQuadric(c) => r.hypot(*c),
+ Basis::InverseMultiQuadric(c) => (r * r + c * c).powf(-0.5),
+ }
+ }
+}
+
+pub struct RadialBasisFunction {
+ // Note: could make basis a type-level parameter
+ basis: Basis,
+ // TODO(explore): use matrix & slicing instead (fewer allocs).
+ // An array of n vectors each of size m.
+ centers: Vec>,
+ // An m x n' matrix, where n' is the number of basis functions (including polynomial),
+ // and m is the number of coords.
+ deltas: DMatrix,
+}
+
+impl RadialBasisFunction {
+ pub fn eval(&self, coords: DVector) -> DVector {
+ let n = self.centers.len();
+ let basis = DVector::from_fn(self.deltas.ncols(), |row, _c| {
+ if row < n {
+ // component from basis functions
+ self.basis.eval((&coords - &self.centers[row]).norm())
+ } else if row == n {
+ // constant component
+ 1.0
+ } else {
+ // linear component
+ coords[row - n - 1]
+ }
+ });
+ &self.deltas * basis
+ }
+
+ // The order for the polynomial part, meaning terms up to (order - 1) are included.
+ // This usage is consistent with Wilna du Toit's masters thesis "Radial Basis
+ // Function Interpolation"
+ // Notice, a PolyHarmonic 2 and order of 2 is a thin plate spline.
+ pub fn create(
+ centers: Vec>,
+ vals: Vec>,
+ basis: Basis,
+ order: usize,
+ ) -> RadialBasisFunction {
+ let n = centers.len();
+ // n x m matrix. There's probably a better way to do this, ah well.
+ let mut vals = DMatrix::from_columns(&vals).transpose();
+ let n_aug = match order {
+ // Pure radial basis functions
+ 0 => n,
+ // Constant term
+ 1 => n + 1,
+ // Affine terms
+ 2 => n + 1 + centers[0].len(),
+ _ => unimplemented!("don't yet support higher order polynomials"),
+ };
+ // Augment to n' x m matrix, where n' is the total number of basis functions.
+ if n_aug > n {
+ vals = vals.resize_vertically(n_aug, 0.0);
+ }
+ // We translate the system to center the mean at the origin so that when
+ // the system is degenerate, the pseudoinverse below minimizes the linear
+ // coefficients.
+ let means: Vec<_> = if order == 2 {
+ let n = centers.len();
+ let n_recip = (n as f64).recip();
+ (0..centers[0].len())
+ .map(|i| centers.iter().map(|c| c[i]).sum::() * n_recip)
+ .collect()
+ } else {
+ Vec::new()
+ };
+ let mat = DMatrix::from_fn(n_aug, n_aug, |r, c| {
+ if r < n && c < n {
+ basis.eval((¢ers[r] - ¢ers[c]).norm())
+ } else if r < n {
+ if c == n {
+ 1.0
+ } else {
+ centers[r][c - n - 1] - means[c - n - 1]
+ }
+ } else if c < n {
+ if r == n {
+ 1.0
+ } else {
+ centers[c][r - n - 1] - means[r - n - 1]
+ }
+ } else {
+ 0.0
+ }
+ });
+ // inv is an n' x n' matrix.
+ let svd = SVD::new(mat, true, true);
+ // Use pseudo-inverse here to get "least squares fit" when there's
+ // no unique result (for example, when dimensionality is too small).
+ let inv = svd.pseudo_inverse(1e-6).expect("error inverting matrix");
+ // Again, this transpose feels like I don't know what I'm doing.
+ let mut deltas = (inv * vals).transpose();
+ if order == 2 {
+ let m = centers[0].len();
+ for i in 0..deltas.nrows() {
+ let offset: f64 = (0..m).map(|j| means[j] * deltas[(i, n + 1 + j)]).sum();
+ deltas[(i, n)] -= offset;
+ }
+ }
+ RadialBasisFunction {
+ basis,
+ centers,
+ deltas,
+ }
+ }
+}
diff --git a/src/tools/data_tools/add_point_coordinates_to_table.rs b/src/tools/data_tools/add_point_coordinates_to_table.rs
index 89836aabc..20ca2e446 100644
--- a/src/tools/data_tools/add_point_coordinates_to_table.rs
+++ b/src/tools/data_tools/add_point_coordinates_to_table.rs
@@ -116,7 +116,7 @@ impl WhiteboxTool for AddPointCoordinatesToTable {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/data_tools/clean_vector.rs b/src/tools/data_tools/clean_vector.rs
index dea55148e..193768d22 100644
--- a/src/tools/data_tools/clean_vector.rs
+++ b/src/tools/data_tools/clean_vector.rs
@@ -127,7 +127,7 @@ impl WhiteboxTool for CleanVector {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/data_tools/convert_nodata_to_zero.rs b/src/tools/data_tools/convert_nodata_to_zero.rs
index 3812fb41c..4b14d5838 100644
--- a/src/tools/data_tools/convert_nodata_to_zero.rs
+++ b/src/tools/data_tools/convert_nodata_to_zero.rs
@@ -126,7 +126,7 @@ impl WhiteboxTool for ConvertNodataToZero {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/data_tools/convert_raster_format.rs b/src/tools/data_tools/convert_raster_format.rs
index 09e592e07..1bba1386d 100644
--- a/src/tools/data_tools/convert_raster_format.rs
+++ b/src/tools/data_tools/convert_raster_format.rs
@@ -122,7 +122,7 @@ impl WhiteboxTool for ConvertRasterFormat {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/data_tools/csv_points_to_vector.rs b/src/tools/data_tools/csv_points_to_vector.rs
index 3dbbf5d25..e3a303cbf 100644
--- a/src/tools/data_tools/csv_points_to_vector.rs
+++ b/src/tools/data_tools/csv_points_to_vector.rs
@@ -161,7 +161,7 @@ impl WhiteboxTool for CsvPointsToVector {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/data_tools/export_table_to_csv.rs b/src/tools/data_tools/export_table_to_csv.rs
index 2695b24dd..e42afda0c 100644
--- a/src/tools/data_tools/export_table_to_csv.rs
+++ b/src/tools/data_tools/export_table_to_csv.rs
@@ -137,7 +137,7 @@ impl WhiteboxTool for ExportTableToCsv {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/data_tools/join_tables.rs b/src/tools/data_tools/join_tables.rs
index 1103c60f6..fbafe1f7e 100644
--- a/src/tools/data_tools/join_tables.rs
+++ b/src/tools/data_tools/join_tables.rs
@@ -191,7 +191,7 @@ impl WhiteboxTool for JoinTables {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/data_tools/lines_to_polygons.rs b/src/tools/data_tools/lines_to_polygons.rs
index 8f37f8f9a..bb4a35c42 100644
--- a/src/tools/data_tools/lines_to_polygons.rs
+++ b/src/tools/data_tools/lines_to_polygons.rs
@@ -134,7 +134,7 @@ impl WhiteboxTool for LinesToPolygons {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/data_tools/merge_table_with_csv.rs b/src/tools/data_tools/merge_table_with_csv.rs
index f1873fc39..f9f0db594 100644
--- a/src/tools/data_tools/merge_table_with_csv.rs
+++ b/src/tools/data_tools/merge_table_with_csv.rs
@@ -195,7 +195,7 @@ impl WhiteboxTool for MergeTableWithCsv {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/data_tools/merge_vectors.rs b/src/tools/data_tools/merge_vectors.rs
index 4d45a8236..fa06ae5cc 100644
--- a/src/tools/data_tools/merge_vectors.rs
+++ b/src/tools/data_tools/merge_vectors.rs
@@ -144,7 +144,7 @@ impl WhiteboxTool for MergeVectors {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/data_tools/modify_nodata_value.rs b/src/tools/data_tools/modify_nodata_value.rs
index ade0e5502..0f55d8d7f 100644
--- a/src/tools/data_tools/modify_nodata_value.rs
+++ b/src/tools/data_tools/modify_nodata_value.rs
@@ -121,7 +121,7 @@ impl WhiteboxTool for ModifyNoDataValue {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/data_tools/multipart_to_singlepart.rs b/src/tools/data_tools/multipart_to_singlepart.rs
index 73f5f7da4..8f17a4e9f 100644
--- a/src/tools/data_tools/multipart_to_singlepart.rs
+++ b/src/tools/data_tools/multipart_to_singlepart.rs
@@ -146,7 +146,7 @@ impl WhiteboxTool for MultiPartToSinglePart {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/data_tools/new_raster.rs b/src/tools/data_tools/new_raster.rs
index 17054d2cd..1b6e02f52 100644
--- a/src/tools/data_tools/new_raster.rs
+++ b/src/tools/data_tools/new_raster.rs
@@ -141,7 +141,7 @@ impl WhiteboxTool for NewRasterFromBase {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/data_tools/polygons_to_lines.rs b/src/tools/data_tools/polygons_to_lines.rs
index 0e99b8668..293bf8100 100644
--- a/src/tools/data_tools/polygons_to_lines.rs
+++ b/src/tools/data_tools/polygons_to_lines.rs
@@ -129,7 +129,7 @@ impl WhiteboxTool for PolygonsToLines {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/data_tools/print_geotiff_tags.rs b/src/tools/data_tools/print_geotiff_tags.rs
index bc5e09f82..a6ec35299 100644
--- a/src/tools/data_tools/print_geotiff_tags.rs
+++ b/src/tools/data_tools/print_geotiff_tags.rs
@@ -122,7 +122,7 @@ impl WhiteboxTool for PrintGeoTiffTags {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/data_tools/raster_to_vector_lines.rs b/src/tools/data_tools/raster_to_vector_lines.rs
index bc8b67a30..6f9caeedb 100644
--- a/src/tools/data_tools/raster_to_vector_lines.rs
+++ b/src/tools/data_tools/raster_to_vector_lines.rs
@@ -142,7 +142,7 @@ impl WhiteboxTool for RasterToVectorLines {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/data_tools/raster_to_vector_points.rs b/src/tools/data_tools/raster_to_vector_points.rs
index f5ef97f0b..3b413f842 100644
--- a/src/tools/data_tools/raster_to_vector_points.rs
+++ b/src/tools/data_tools/raster_to_vector_points.rs
@@ -130,7 +130,7 @@ impl WhiteboxTool for RasterToVectorPoints {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/data_tools/reinitialize_attribute_table.rs b/src/tools/data_tools/reinitialize_attribute_table.rs
index b3a802606..9e6b8184e 100644
--- a/src/tools/data_tools/reinitialize_attribute_table.rs
+++ b/src/tools/data_tools/reinitialize_attribute_table.rs
@@ -115,7 +115,7 @@ impl WhiteboxTool for ReinitializeAttributeTable {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/data_tools/remove_polygon_holes.rs b/src/tools/data_tools/remove_polygon_holes.rs
index d15e8ef67..2ca8ccf64 100644
--- a/src/tools/data_tools/remove_polygon_holes.rs
+++ b/src/tools/data_tools/remove_polygon_holes.rs
@@ -129,7 +129,7 @@ impl WhiteboxTool for RemovePolygonHoles {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/data_tools/set_nodata_value.rs b/src/tools/data_tools/set_nodata_value.rs
index e42784dd5..e50a000d0 100644
--- a/src/tools/data_tools/set_nodata_value.rs
+++ b/src/tools/data_tools/set_nodata_value.rs
@@ -147,7 +147,7 @@ impl WhiteboxTool for SetNodataValue {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/data_tools/singlepart_to_multipart.rs b/src/tools/data_tools/singlepart_to_multipart.rs
index db7503eaf..2ad30d43c 100644
--- a/src/tools/data_tools/singlepart_to_multipart.rs
+++ b/src/tools/data_tools/singlepart_to_multipart.rs
@@ -157,7 +157,7 @@ impl WhiteboxTool for SinglePartToMultiPart {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/data_tools/vector_lines_to_raster.rs b/src/tools/data_tools/vector_lines_to_raster.rs
index 270ebe04a..6cf8a8e38 100644
--- a/src/tools/data_tools/vector_lines_to_raster.rs
+++ b/src/tools/data_tools/vector_lines_to_raster.rs
@@ -185,7 +185,7 @@ impl WhiteboxTool for VectorLinesToRaster {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/data_tools/vector_points_to_raster.rs b/src/tools/data_tools/vector_points_to_raster.rs
index 7181dc5e3..e96ef9e6e 100644
--- a/src/tools/data_tools/vector_points_to_raster.rs
+++ b/src/tools/data_tools/vector_points_to_raster.rs
@@ -190,7 +190,7 @@ impl WhiteboxTool for VectorPointsToRaster {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/data_tools/vector_polygons_to_raster.rs b/src/tools/data_tools/vector_polygons_to_raster.rs
index 5d9b5ef17..1061416c0 100644
--- a/src/tools/data_tools/vector_polygons_to_raster.rs
+++ b/src/tools/data_tools/vector_polygons_to_raster.rs
@@ -163,7 +163,7 @@ impl WhiteboxTool for VectorPolygonsToRaster {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/gis_analysis/aggregate_raster.rs b/src/tools/gis_analysis/aggregate_raster.rs
index 4e68ce3ca..11001a8e6 100644
--- a/src/tools/gis_analysis/aggregate_raster.rs
+++ b/src/tools/gis_analysis/aggregate_raster.rs
@@ -151,7 +151,7 @@ impl WhiteboxTool for AggregateRaster {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/gis_analysis/average_overlay.rs b/src/tools/gis_analysis/average_overlay.rs
index d8f7ff4ed..b2a70a8e7 100644
--- a/src/tools/gis_analysis/average_overlay.rs
+++ b/src/tools/gis_analysis/average_overlay.rs
@@ -125,7 +125,7 @@ impl WhiteboxTool for AverageOverlay {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/gis_analysis/block_maximum.rs b/src/tools/gis_analysis/block_maximum.rs
index e82e60392..498669f10 100644
--- a/src/tools/gis_analysis/block_maximum.rs
+++ b/src/tools/gis_analysis/block_maximum.rs
@@ -2,7 +2,7 @@
This tool is part of the WhiteboxTools geospatial analysis library.
Authors: Dr. John Lindsay
Created: 09/10/2018
-Last Modified: 18/10/2019
+Last Modified: 09/12/2019
License: MIT
*/
@@ -158,7 +158,7 @@ impl WhiteboxTool for BlockMaximumGridding {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
@@ -251,7 +251,8 @@ impl WhiteboxTool for BlockMaximumGridding {
if !base_file.contains(&sep) && !base_file.contains("/") {
base_file = format!("{}{}", working_directory, base_file);
}
- let base = Raster::new(&base_file, "r")?;
+ let mut base = Raster::new(&base_file, "r")?;
+ base.configs.nodata = nodata;
Raster::initialize_using_file(&output_file, &base)
} else {
// base the output raster on the grid_res and the
diff --git a/src/tools/gis_analysis/block_minimum.rs b/src/tools/gis_analysis/block_minimum.rs
index aa1a9b6f7..e8c517d9e 100644
--- a/src/tools/gis_analysis/block_minimum.rs
+++ b/src/tools/gis_analysis/block_minimum.rs
@@ -2,7 +2,7 @@
This tool is part of the WhiteboxTools geospatial analysis library.
Authors: Dr. John Lindsay
Created: 09/10/2018
-Last Modified: 18/10/2019
+Last Modified: 09/12/2019
License: MIT
*/
@@ -158,7 +158,7 @@ impl WhiteboxTool for BlockMinimumGridding {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
@@ -251,7 +251,8 @@ impl WhiteboxTool for BlockMinimumGridding {
if !base_file.contains(&sep) && !base_file.contains("/") {
base_file = format!("{}{}", working_directory, base_file);
}
- let base = Raster::new(&base_file, "r")?;
+ let mut base = Raster::new(&base_file, "r")?;
+ base.configs.nodata = nodata;
Raster::initialize_using_file(&output_file, &base)
} else {
// base the output raster on the grid_res and the
diff --git a/src/tools/gis_analysis/boundary_shape_complexity.rs b/src/tools/gis_analysis/boundary_shape_complexity.rs
index 2d8d3c9f1..316fff0e8 100644
--- a/src/tools/gis_analysis/boundary_shape_complexity.rs
+++ b/src/tools/gis_analysis/boundary_shape_complexity.rs
@@ -135,7 +135,7 @@ impl WhiteboxTool for BoundaryShapeComplexity {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/gis_analysis/buffer_raster.rs b/src/tools/gis_analysis/buffer_raster.rs
index 153ecdf18..ac9832c19 100644
--- a/src/tools/gis_analysis/buffer_raster.rs
+++ b/src/tools/gis_analysis/buffer_raster.rs
@@ -160,7 +160,7 @@ impl WhiteboxTool for BufferRaster {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/gis_analysis/buffer_vector.rs b/src/tools/gis_analysis/buffer_vector.rs
index d5a43f027..cc8641286 100644
--- a/src/tools/gis_analysis/buffer_vector.rs
+++ b/src/tools/gis_analysis/buffer_vector.rs
@@ -173,7 +173,7 @@
// if args.len() == 0 {
// return Err(Error::new(
// ErrorKind::InvalidInput,
-// "Tool run with no paramters.",
+// "Tool run with no parameters.",
// ));
// }
// for i in 0..args.len() {
diff --git a/src/tools/gis_analysis/centroid.rs b/src/tools/gis_analysis/centroid.rs
index 102652afe..82e410e40 100644
--- a/src/tools/gis_analysis/centroid.rs
+++ b/src/tools/gis_analysis/centroid.rs
@@ -133,7 +133,7 @@ impl WhiteboxTool for Centroid {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/gis_analysis/centroid_vector.rs b/src/tools/gis_analysis/centroid_vector.rs
index aec6c1b77..151c1fb0b 100644
--- a/src/tools/gis_analysis/centroid_vector.rs
+++ b/src/tools/gis_analysis/centroid_vector.rs
@@ -137,7 +137,7 @@ impl WhiteboxTool for CentroidVector {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/gis_analysis/clip.rs b/src/tools/gis_analysis/clip.rs
index 00f969a68..e9ea64a8c 100644
--- a/src/tools/gis_analysis/clip.rs
+++ b/src/tools/gis_analysis/clip.rs
@@ -164,7 +164,7 @@ impl WhiteboxTool for Clip {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
@@ -637,8 +637,7 @@ impl WhiteboxTool for Clip {
// Break the polygons up into lines at junction points.
let dimensions = 2;
let capacity_per_node = 64;
- let mut snap_tree =
- KdTree::new_with_capacity(dimensions, capacity_per_node);
+ let mut snap_tree = KdTree::with_capacity(dimensions, capacity_per_node);
let mut p: Point2D;
for i in 0..polygons.len() {
for j in 0..polygons[i].len() {
@@ -780,7 +779,7 @@ impl WhiteboxTool for Clip {
// now add the endpoints of each polyline into a kd tree
let dimensions = 2;
let capacity_per_node = 64;
- let mut kdtree = KdTree::new_with_capacity(dimensions, capacity_per_node);
+ let mut kdtree = KdTree::with_capacity(dimensions, capacity_per_node);
let mut p1: Point2D;
let mut p2: Point2D;
let mut p3: Point2D;
diff --git a/src/tools/gis_analysis/clip_raster_to_polygon.rs b/src/tools/gis_analysis/clip_raster_to_polygon.rs
index 575e57e46..5bf77dcfb 100644
--- a/src/tools/gis_analysis/clip_raster_to_polygon.rs
+++ b/src/tools/gis_analysis/clip_raster_to_polygon.rs
@@ -148,7 +148,7 @@ impl WhiteboxTool for ClipRasterToPolygon {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/gis_analysis/clump.rs b/src/tools/gis_analysis/clump.rs
index d14d17cd4..30a641e68 100644
--- a/src/tools/gis_analysis/clump.rs
+++ b/src/tools/gis_analysis/clump.rs
@@ -152,7 +152,7 @@ impl WhiteboxTool for Clump {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
@@ -224,7 +224,6 @@ impl WhiteboxTool for Clump {
output.configs.nodata = out_nodata;
output.configs.photometric_interp = PhotometricInterpretation::Categorical;
output.configs.data_type = DataType::I32;
- // output.configs.data_type = DataType::F32;
let mut dx = [1, 1, 1, 0, -1, -1, -1, 0];
let mut dy = [-1, 0, 1, 1, 1, 0, -1, -1];
diff --git a/src/tools/gis_analysis/compactness_ratio.rs b/src/tools/gis_analysis/compactness_ratio.rs
index 559c4ca28..0624d3740 100644
--- a/src/tools/gis_analysis/compactness_ratio.rs
+++ b/src/tools/gis_analysis/compactness_ratio.rs
@@ -127,7 +127,7 @@ impl WhiteboxTool for CompactnessRatio {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/gis_analysis/construct_vector_tin.rs b/src/tools/gis_analysis/construct_vector_tin.rs
index 317f381be..ed1db7a1f 100644
--- a/src/tools/gis_analysis/construct_vector_tin.rs
+++ b/src/tools/gis_analysis/construct_vector_tin.rs
@@ -2,7 +2,7 @@
This tool is part of the WhiteboxTools geospatial analysis library.
Authors: Dr. John Lindsay
Created: 21/09/2018
-Last Modified: 18/10/2019
+Last Modified: 07/12/2019
License: MIT
*/
@@ -24,6 +24,10 @@ use std::path;
/// or alternatively, if the vector is of a z-dimension *ShapeTypeDimension*, the point z-values may be
/// used for vertex heights (`--use_z`). For LiDAR points, use the `LidarConstructVectorTIN` tool instead.
///
+/// Triangulation often creates very long, narrow triangles near the edges of the data coverage, particularly
+/// in convex regions along the data boundary. To avoid these spurious triangles, the user may optionally
+/// specify the maximum allowable edge length of a triangular facet (`--max_triangle_edge_length`).
+///
/// # See Also
/// `LidarConstructVectorTIN`
pub struct ConstructVectorTIN {
@@ -89,6 +93,15 @@ impl ConstructVectorTIN {
optional: false,
});
+ parameters.push(ToolParameter {
+ name: "Maximum Triangle Edge Length (optional)".to_owned(),
+ flags: vec!["--max_triangle_edge_length".to_owned()],
+ description: "Optional maximum triangle edge length; triangles larger than this size will not be gridded.".to_owned(),
+ parameter_type: ParameterType::Float,
+ default_value: None,
+ optional: true,
+ });
+
let sep: String = path::MAIN_SEPARATOR.to_string();
let p = format!("{}", env::current_dir().unwrap().display());
let e = format!("{}", env::current_exe().unwrap().display());
@@ -163,12 +176,13 @@ impl WhiteboxTool for ConstructVectorTIN {
let mut use_z = false;
let mut use_field = false;
let mut output_file: String = "".to_string();
+ let mut max_triangle_edge_length = f64::INFINITY;
// read the arguments
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
@@ -204,6 +218,14 @@ impl WhiteboxTool for ConstructVectorTIN {
} else {
args[i + 1].to_string()
};
+ } else if flag_val == "-max_triangle_edge_length" {
+ max_triangle_edge_length = if keyval {
+ vec[1].to_string().parse::().unwrap()
+ } else {
+ args[i + 1].to_string().parse::().unwrap()
+ };
+
+ max_triangle_edge_length *= max_triangle_edge_length; // actually squared distance
}
}
@@ -350,64 +372,68 @@ impl WhiteboxTool for ConstructVectorTIN {
p2 = result.triangles[i + 1];
p3 = result.triangles[i];
- let mut tri_points: Vec = Vec::with_capacity(4);
- tri_points.push(points[p1].clone());
- tri_points.push(points[p2].clone());
- tri_points.push(points[p3].clone());
- tri_points.push(points[p1].clone());
-
- let mut sfg = ShapefileGeometry::new(ShapeType::Polygon);
- sfg.add_part(&tri_points);
- output.add_record(sfg);
-
- if use_field || use_z {
- // calculate the hillshade value
- let a = Vector3::new(tri_points[0].x, tri_points[0].y, z_values[p1]);
- let b = Vector3::new(tri_points[1].x, tri_points[1].y, z_values[p2]);
- let c = Vector3::new(tri_points[2].x, tri_points[2].y, z_values[p3]);
- let norm = (b - a).cross(&(c - a)); //).normalize();
- let centroid = (a + b + c) / 3f64;
- // k = -(tri_points[0].x * norm.x + tri_points[0].y * norm.y + norm.z * z_values[p1]);
- // centroid_z = -(norm.x * centroid.x + norm.y * centroid.y + k) / norm.z;
-
- hillshade = 0f64;
- if norm.z != 0f64 {
- fx = -norm.x / norm.z;
- fy = -norm.y / norm.z;
- if fx != 0f64 {
- tan_slope = (fx * fx + fy * fy).sqrt();
- aspect = (180f64 - ((fy / fx).atan()).to_degrees()
- + 90f64 * (fx / (fx).abs()))
- .to_radians();
- term1 = tan_slope / (1f64 + tan_slope * tan_slope).sqrt();
- term2 = sin_theta / tan_slope;
- term3 = cos_theta * (azimuth - aspect).sin();
- hillshade = term1 * (term2 - term3);
- } else {
- hillshade = 0.5;
- }
- hillshade = hillshade * 1024f64;
- if hillshade < 0f64 {
- hillshade = 0f64;
+ if max_distance_squared(points[p1], points[p2], points[p3], z_values[p1],
+ z_values[p2], z_values[p3]) < max_triangle_edge_length {
+
+ let mut tri_points: Vec = Vec::with_capacity(4);
+ tri_points.push(points[p1].clone());
+ tri_points.push(points[p2].clone());
+ tri_points.push(points[p3].clone());
+ tri_points.push(points[p1].clone());
+
+ let mut sfg = ShapefileGeometry::new(ShapeType::Polygon);
+ sfg.add_part(&tri_points);
+ output.add_record(sfg);
+
+ if use_field || use_z {
+ // calculate the hillshade value
+ let a = Vector3::new(tri_points[0].x, tri_points[0].y, z_values[p1]);
+ let b = Vector3::new(tri_points[1].x, tri_points[1].y, z_values[p2]);
+ let c = Vector3::new(tri_points[2].x, tri_points[2].y, z_values[p3]);
+ let norm = (b - a).cross(&(c - a)); //).normalize();
+ let centroid = (a + b + c) / 3f64;
+ // k = -(tri_points[0].x * norm.x + tri_points[0].y * norm.y + norm.z * z_values[p1]);
+ // centroid_z = -(norm.x * centroid.x + norm.y * centroid.y + k) / norm.z;
+
+ hillshade = 0f64;
+ if norm.z != 0f64 {
+ fx = -norm.x / norm.z;
+ fy = -norm.y / norm.z;
+ if fx != 0f64 {
+ tan_slope = (fx * fx + fy * fy).sqrt();
+ aspect = (180f64 - ((fy / fx).atan()).to_degrees()
+ + 90f64 * (fx / (fx).abs()))
+ .to_radians();
+ term1 = tan_slope / (1f64 + tan_slope * tan_slope).sqrt();
+ term2 = sin_theta / tan_slope;
+ term3 = cos_theta * (azimuth - aspect).sin();
+ hillshade = term1 * (term2 - term3);
+ } else {
+ hillshade = 0.5;
+ }
+ hillshade = hillshade * 1024f64;
+ if hillshade < 0f64 {
+ hillshade = 0f64;
+ }
}
+
+ output.attributes.add_record(
+ vec![
+ FieldData::Int(rec_num),
+ FieldData::Real(centroid.z),
+ FieldData::Int(hillshade as i32),
+ ],
+ false,
+ );
+ } else {
+ output
+ .attributes
+ .add_record(vec![FieldData::Int(rec_num)], false);
}
- output.attributes.add_record(
- vec![
- FieldData::Int(rec_num),
- FieldData::Real(centroid.z),
- FieldData::Int(hillshade as i32),
- ],
- false,
- );
- } else {
- output
- .attributes
- .add_record(vec![FieldData::Int(rec_num)], false);
+ rec_num += 1i32;
}
- rec_num += 1i32;
-
if verbose {
progress = (100.0_f64 * i as f64 / (result.triangles.len() - 1) as f64) as usize;
if progress != old_progress {
@@ -438,3 +464,31 @@ impl WhiteboxTool for ConstructVectorTIN {
Ok(())
}
}
+
+/// Calculate squared Euclidean distance between the point and another.
+pub fn max_distance_squared(p1: Point2D, p2: Point2D, p3: Point2D, z1: f64, z2: f64, z3: f64) -> f64 {
+ let mut dx = p1.x - p2.x;
+ let mut dy = p1.y - p2.y;
+ let mut dz = z1 - z2;
+ let mut max_dist = dx * dx + dy * dy + dz * dz;
+
+ dx = p1.x - p3.x;
+ dy = p1.y - p3.y;
+ dz = z1 - z3;
+ let mut dist = dx * dx + dy * dy + dz * dz;
+
+ if dist > max_dist {
+ max_dist = dist
+ }
+
+ dx = p2.x - p3.x;
+ dy = p2.y - p3.y;
+ dz = z2 - z3;
+ dist = dx * dx + dy * dy + dz * dz;
+
+ if dist > max_dist {
+ max_dist = dist
+ }
+
+ max_dist
+}
\ No newline at end of file
diff --git a/src/tools/gis_analysis/cost_allocation.rs b/src/tools/gis_analysis/cost_allocation.rs
index 6dd6d6f4e..88158a2aa 100644
--- a/src/tools/gis_analysis/cost_allocation.rs
+++ b/src/tools/gis_analysis/cost_allocation.rs
@@ -1,7 +1,7 @@
/*
This tool is part of the WhiteboxTools geospatial analysis library.
Authors: Dr. John Lindsay
-Created: July 4, 2017
+Created: 04/072017
Last Modified: 13/10/2018
License: MIT
@@ -137,7 +137,7 @@ impl WhiteboxTool for CostAllocation {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/gis_analysis/cost_distance.rs b/src/tools/gis_analysis/cost_distance.rs
index 75805836e..266e48fd9 100644
--- a/src/tools/gis_analysis/cost_distance.rs
+++ b/src/tools/gis_analysis/cost_distance.rs
@@ -1,7 +1,7 @@
/*
This tool is part of the WhiteboxTools geospatial analysis library.
Authors: Dr. John Lindsay
-Created: July 4, 2017
+Created: 04/07/2017
Last Modified: 15/11/2018
License: MIT
@@ -167,7 +167,7 @@ impl WhiteboxTool for CostDistance {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/gis_analysis/cost_pathway.rs b/src/tools/gis_analysis/cost_pathway.rs
index b1983363e..2d6e85e1e 100644
--- a/src/tools/gis_analysis/cost_pathway.rs
+++ b/src/tools/gis_analysis/cost_pathway.rs
@@ -147,7 +147,7 @@ impl WhiteboxTool for CostPathway {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/gis_analysis/count_if.rs b/src/tools/gis_analysis/count_if.rs
index 54ce857cc..96b00a407 100644
--- a/src/tools/gis_analysis/count_if.rs
+++ b/src/tools/gis_analysis/count_if.rs
@@ -130,7 +130,7 @@ impl WhiteboxTool for CountIf {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/gis_analysis/create_hexagonal_vector_grid.rs b/src/tools/gis_analysis/create_hexagonal_vector_grid.rs
index 2732ec31c..9f5309593 100644
--- a/src/tools/gis_analysis/create_hexagonal_vector_grid.rs
+++ b/src/tools/gis_analysis/create_hexagonal_vector_grid.rs
@@ -159,7 +159,7 @@ impl WhiteboxTool for CreateHexagonalVectorGrid {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/gis_analysis/create_plane.rs b/src/tools/gis_analysis/create_plane.rs
index dc56e4727..cd092aca9 100644
--- a/src/tools/gis_analysis/create_plane.rs
+++ b/src/tools/gis_analysis/create_plane.rs
@@ -155,7 +155,7 @@ impl WhiteboxTool for CreatePlane {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/gis_analysis/create_rectangular_vector_grid.rs b/src/tools/gis_analysis/create_rectangular_vector_grid.rs
index 7df54e236..7a7d7557e 100644
--- a/src/tools/gis_analysis/create_rectangular_vector_grid.rs
+++ b/src/tools/gis_analysis/create_rectangular_vector_grid.rs
@@ -176,7 +176,7 @@ impl WhiteboxTool for CreateRectangularVectorGrid {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/gis_analysis/difference.rs b/src/tools/gis_analysis/difference.rs
index 9aac6117b..272803250 100644
--- a/src/tools/gis_analysis/difference.rs
+++ b/src/tools/gis_analysis/difference.rs
@@ -175,7 +175,7 @@ impl WhiteboxTool for Difference {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
@@ -294,7 +294,7 @@ impl WhiteboxTool for Difference {
// place the points from both files into a KD-tree
let dimensions = 2;
let capacity_per_node = 64;
- let mut tree = KdTree::new_with_capacity(dimensions, capacity_per_node);
+ let mut tree = KdTree::with_capacity(dimensions, capacity_per_node);
let mut p: Point2D;
for record_num in 0..input.num_records {
let record = input.get_record(record_num);
@@ -347,7 +347,7 @@ impl WhiteboxTool for Difference {
// place the points from both files into a KD-tree
let dimensions = 2;
let capacity_per_node = 64;
- let mut tree = KdTree::new_with_capacity(dimensions, capacity_per_node);
+ let mut tree = KdTree::with_capacity(dimensions, capacity_per_node);
let mut p: Point2D;
let mut total_points = 0;
for record_num in 0..input.num_records {
@@ -510,7 +510,7 @@ impl WhiteboxTool for Difference {
// Break the polylines up into shorter lines at junction points.
let dimensions = 2;
let capacity_per_node = 64;
- let mut tree = KdTree::new_with_capacity(dimensions, capacity_per_node);
+ let mut tree = KdTree::with_capacity(dimensions, capacity_per_node);
let mut p: Point2D;
for i in 0..polylines.len() {
for j in 0..polylines[i].len() {
@@ -742,8 +742,7 @@ impl WhiteboxTool for Difference {
// Break the polygons up into lines at junction points.
let dimensions = 2;
let capacity_per_node = 64;
- let mut snap_tree =
- KdTree::new_with_capacity(dimensions, capacity_per_node);
+ let mut snap_tree = KdTree::with_capacity(dimensions, capacity_per_node);
let mut p: Point2D;
for i in 0..polygons.len() {
for j in 0..polygons[i].len() {
@@ -902,8 +901,7 @@ impl WhiteboxTool for Difference {
// now add the endpoints of each polyline into a kd tree
let dimensions = 2;
let capacity_per_node = 64;
- let mut kdtree =
- KdTree::new_with_capacity(dimensions, capacity_per_node);
+ let mut kdtree = KdTree::with_capacity(dimensions, capacity_per_node);
let mut p1: Point2D;
let mut p2: Point2D;
let mut p3: Point2D;
diff --git a/src/tools/gis_analysis/dissolve.rs b/src/tools/gis_analysis/dissolve.rs
index 94c1435da..15cb0237c 100644
--- a/src/tools/gis_analysis/dissolve.rs
+++ b/src/tools/gis_analysis/dissolve.rs
@@ -169,7 +169,7 @@ impl WhiteboxTool for Dissolve {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
@@ -338,7 +338,7 @@ impl WhiteboxTool for Dissolve {
};
let dimensions = 2;
let capacity_per_node = 64;
- let mut tree = KdTree::new_with_capacity(dimensions, capacity_per_node);
+ let mut tree = KdTree::with_capacity(dimensions, capacity_per_node);
for i in 0..num_polygons {
for j in 0..polygons[i].len() {
p = polygons[i][j];
@@ -535,7 +535,7 @@ impl WhiteboxTool for Dissolve {
// now add the endpoints of each polyline into a kd tree
let dimensions = 2;
let capacity_per_node = 64;
- let mut kdtree = KdTree::new_with_capacity(dimensions, capacity_per_node);
+ let mut kdtree = KdTree::with_capacity(dimensions, capacity_per_node);
let mut p1: Point2D;
let mut p2: Point2D;
let mut p3: Point2D;
diff --git a/src/tools/gis_analysis/edge_proportion.rs b/src/tools/gis_analysis/edge_proportion.rs
index 836a87ac8..4287b82f3 100644
--- a/src/tools/gis_analysis/edge_proportion.rs
+++ b/src/tools/gis_analysis/edge_proportion.rs
@@ -141,7 +141,7 @@ impl WhiteboxTool for EdgeProportion {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/gis_analysis/eliminate_coincident_points.rs b/src/tools/gis_analysis/eliminate_coincident_points.rs
index e067e89a0..cd02d3130 100644
--- a/src/tools/gis_analysis/eliminate_coincident_points.rs
+++ b/src/tools/gis_analysis/eliminate_coincident_points.rs
@@ -147,7 +147,7 @@ impl WhiteboxTool for EliminateCoincidentPoints {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/gis_analysis/elongation_ratio.rs b/src/tools/gis_analysis/elongation_ratio.rs
index f0c7842cb..6f229ee55 100644
--- a/src/tools/gis_analysis/elongation_ratio.rs
+++ b/src/tools/gis_analysis/elongation_ratio.rs
@@ -129,7 +129,7 @@ impl WhiteboxTool for ElongationRatio {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/gis_analysis/erase.rs b/src/tools/gis_analysis/erase.rs
index 4d5c1db4d..31dd66188 100644
--- a/src/tools/gis_analysis/erase.rs
+++ b/src/tools/gis_analysis/erase.rs
@@ -161,7 +161,7 @@ impl WhiteboxTool for Erase {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
@@ -636,8 +636,7 @@ impl WhiteboxTool for Erase {
// Break the polygons up into lines at junction points.
let dimensions = 2;
let capacity_per_node = 64;
- let mut snap_tree =
- KdTree::new_with_capacity(dimensions, capacity_per_node);
+ let mut snap_tree = KdTree::with_capacity(dimensions, capacity_per_node);
let mut p: Point2D;
for i in 0..polygons.len() {
for j in 0..polygons[i].len() {
@@ -779,7 +778,7 @@ impl WhiteboxTool for Erase {
// now add the endpoints of each polyline into a kd tree
let dimensions = 2;
let capacity_per_node = 64;
- let mut kdtree = KdTree::new_with_capacity(dimensions, capacity_per_node);
+ let mut kdtree = KdTree::with_capacity(dimensions, capacity_per_node);
let mut p1: Point2D;
let mut p2: Point2D;
let mut p3: Point2D;
diff --git a/src/tools/gis_analysis/erase_polygon_from_raster.rs b/src/tools/gis_analysis/erase_polygon_from_raster.rs
index 78ff9cdad..d5d8f444c 100644
--- a/src/tools/gis_analysis/erase_polygon_from_raster.rs
+++ b/src/tools/gis_analysis/erase_polygon_from_raster.rs
@@ -132,7 +132,7 @@ impl WhiteboxTool for ErasePolygonFromRaster {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/gis_analysis/euclidean_allocation.rs b/src/tools/gis_analysis/euclidean_allocation.rs
index 245b92f95..6ea17d390 100644
--- a/src/tools/gis_analysis/euclidean_allocation.rs
+++ b/src/tools/gis_analysis/euclidean_allocation.rs
@@ -128,7 +128,7 @@ impl WhiteboxTool for EuclideanAllocation {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/gis_analysis/euclidean_distance.rs b/src/tools/gis_analysis/euclidean_distance.rs
index 96a708dfe..d47c19c78 100644
--- a/src/tools/gis_analysis/euclidean_distance.rs
+++ b/src/tools/gis_analysis/euclidean_distance.rs
@@ -1,8 +1,8 @@
/*
This tool is part of the WhiteboxTools geospatial analysis library.
Authors: Dr. John Lindsay
-Created: June 22 2017
-Last Modified: 25/11/2018
+Created: 22/06/2017
+Last Modified: 05/12/2019
License: MIT
*/
@@ -137,7 +137,7 @@ impl WhiteboxTool for EuclideanDistance {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
@@ -149,18 +149,19 @@ impl WhiteboxTool for EuclideanDistance {
if vec.len() > 1 {
keyval = true;
}
- if vec[0].to_lowercase() == "-i" || vec[0].to_lowercase() == "--input" {
- if keyval {
- input_file = vec[1].to_string();
+ let flag_val = vec[0].to_lowercase().replace("--", "-");
+ if flag_val == "-i" || flag_val == "-input" {
+ input_file = if keyval {
+ vec[1].to_string()
} else {
- input_file = args[i + 1].to_string();
- }
- } else if vec[0].to_lowercase() == "-o" || vec[0].to_lowercase() == "--output" {
- if keyval {
- output_file = vec[1].to_string();
+ args[i + 1].to_string()
+ };
+ } else if flag_val == "-o" || flag_val == "-output" {
+ output_file = if keyval {
+ vec[1].to_string()
} else {
- output_file = args[i + 1].to_string();
- }
+ args[i + 1].to_string()
+ };
}
}
@@ -191,31 +192,31 @@ impl WhiteboxTool for EuclideanDistance {
let nodata = input.configs.nodata;
let rows = input.configs.rows as isize;
let columns = input.configs.columns as isize;
- let mut r_x: Array2D = Array2D::new(rows, columns, 0f64, nodata)?;
- let mut r_y: Array2D = Array2D::new(rows, columns, 0f64, nodata)?;
-
+
let start = Instant::now();
+ let mut rx: Array2D = Array2D::new(rows, columns, 0f64, nodata)?;
+ let mut ry: Array2D = Array2D::new(rows, columns, 0f64, nodata)?;
+
let mut output = Raster::initialize_using_file(&output_file, &input);
output.configs.data_type = DataType::F32;
let mut h: f64;
let mut which_cell: usize;
let inf_val = f64::INFINITY;
- let d_x = [-1, -1, 0, 1, 1, 1, 0, -1];
- let d_y = [0, -1, -1, -1, 0, 1, 1, 1];
- let g_x = [1.0, 1.0, 0.0, 1.0, 1.0, 1.0, 0.0, 1.0];
- let g_y = [0.0, 1.0, 1.0, 1.0, 0.0, 1.0, 1.0, 1.0];
+ let dx = [-1, -1, 0, 1, 1, 1, 0, -1];
+ let dy = [0, -1, -1, -1, 0, 1, 1, 1];
+ let gx = [1.0, 1.0, 0.0, 1.0, 1.0, 1.0, 0.0, 1.0];
+ let gy = [0.0, 1.0, 1.0, 1.0, 0.0, 1.0, 1.0, 1.0];
let (mut x, mut y): (isize, isize);
let (mut z, mut z2, mut z_min): (f64, f64, f64);
for row in 0..rows {
for col in 0..columns {
- z = input[(row, col)];
- if z != 0.0 {
- output[(row, col)] = 0.0;
+ if input.get_value(row, col) != 0.0 {
+ output.set_value(row, col, 0.0);
} else {
- output[(row, col)] = inf_val;
+ output.set_value(row, col, inf_val);
}
}
if verbose {
@@ -229,20 +230,20 @@ impl WhiteboxTool for EuclideanDistance {
for row in 0..rows {
for col in 0..columns {
- z = output[(row, col)];
+ z = output.get_value(row, col);
if z != 0.0 {
z_min = inf_val;
which_cell = 0;
for i in 0..4 {
- x = col + d_x[i];
- y = row + d_y[i];
- z2 = output[(y, x)];
+ x = col + dx[i];
+ y = row + dy[i];
+ z2 = output.get_value(y, x);
if z2 != nodata {
h = match i {
- 0 => 2.0 * r_x[(y, x)] + 1.0,
- 1 => 2.0 * (r_x[(y, x)] + r_y[(y, x)] + 1.0),
- 2 => 2.0 * r_y[(y, x)] + 1.0,
- _ => 2.0 * (r_x[(y, x)] + r_y[(y, x)] + 1.0), // 3
+ 0 => 2.0 * rx.get_value(y, x) + 1.0,
+ 1 => 2.0 * (rx.get_value(y, x) + ry.get_value(y, x) + 1.0),
+ 2 => 2.0 * ry.get_value(y, x) + 1.0,
+ _ => 2.0 * (rx.get_value(y, x) + ry.get_value(y, x) + 1.0), // 3
};
z2 += h;
if z2 < z_min {
@@ -252,11 +253,11 @@ impl WhiteboxTool for EuclideanDistance {
}
}
if z_min < z {
- output[(row, col)] = z_min;
- x = col + d_x[which_cell];
- y = row + d_y[which_cell];
- r_x[(row, col)] = r_x[(y, x)] + g_x[which_cell];
- r_y[(row, col)] = r_y[(y, x)] + g_y[which_cell];
+ output.set_value(row, col, z_min);
+ x = col + dx[which_cell];
+ y = row + dy[which_cell];
+ rx.set_value(row, col, rx.get_value(y, x) + gx[which_cell]);
+ ry.set_value(row, col, ry.get_value(y, x) + gy[which_cell]);
}
}
}
@@ -271,20 +272,20 @@ impl WhiteboxTool for EuclideanDistance {
for row in (0..rows).rev() {
for col in (0..columns).rev() {
- z = output[(row, col)];
+ z = output.get_value(row, col);
if z != 0.0 {
z_min = inf_val;
which_cell = 0;
for i in 4..8 {
- x = col + d_x[i];
- y = row + d_y[i];
- z2 = output[(y, x)];
+ x = col + dx[i];
+ y = row + dy[i];
+ z2 = output.get_value(y, x);
if z2 != nodata {
h = match i {
- 5 => 2.0 * (r_x[(y, x)] + r_y[(y, x)] + 1.0),
- 4 => 2.0 * r_x[(y, x)] + 1.0,
- 6 => 2.0 * r_y[(y, x)] + 1.0,
- _ => 2.0 * (r_x[(y, x)] + r_y[(y, x)] + 1.0), // 7
+ 5 => 2.0 * (rx.get_value(y, x) + ry.get_value(y, x) + 1.0),
+ 4 => 2.0 * rx.get_value(y, x) + 1.0,
+ 6 => 2.0 * ry.get_value(y, x) + 1.0,
+ _ => 2.0 * (rx.get_value(y, x) + ry.get_value(y, x) + 1.0), // 7
};
z2 += h;
if z2 < z_min {
@@ -295,10 +296,10 @@ impl WhiteboxTool for EuclideanDistance {
}
if z_min < z {
output[(row, col)] = z_min;
- x = col + d_x[which_cell];
- y = row + d_y[which_cell];
- r_x[(row, col)] = r_x[(y, x)] + g_x[which_cell];
- r_y[(row, col)] = r_y[(y, x)] + g_y[which_cell];
+ x = col + dx[which_cell];
+ y = row + dy[which_cell];
+ rx.set_value(row, col, rx.get_value(y, x) + gx[which_cell]);
+ ry.set_value(row, col, ry.get_value(y, x) + gy[which_cell]);
}
}
}
@@ -314,11 +315,10 @@ impl WhiteboxTool for EuclideanDistance {
let cell_size = (input.configs.resolution_x + input.configs.resolution_y) / 2.0;
for row in 0..rows {
for col in 0..columns {
- z = input[(row, col)];
- if z != nodata {
- output[(row, col)] = output[(row, col)].sqrt() * cell_size;
+ if input.get_value(row, col) != nodata {
+ output.set_value(row, col, output.get_value(row, col).sqrt() * cell_size);
} else {
- output[(row, col)] = nodata;
+ output.set_value(row, col, nodata);
}
}
if verbose {
diff --git a/src/tools/gis_analysis/extend_vector_lines.rs b/src/tools/gis_analysis/extend_vector_lines.rs
index c1db8e8d3..0268996fa 100644
--- a/src/tools/gis_analysis/extend_vector_lines.rs
+++ b/src/tools/gis_analysis/extend_vector_lines.rs
@@ -153,7 +153,7 @@ impl WhiteboxTool for ExtendVectorLines {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/gis_analysis/extract_nodes.rs b/src/tools/gis_analysis/extract_nodes.rs
index b9766e738..2944f589b 100644
--- a/src/tools/gis_analysis/extract_nodes.rs
+++ b/src/tools/gis_analysis/extract_nodes.rs
@@ -128,7 +128,7 @@ impl WhiteboxTool for ExtractNodes {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/gis_analysis/extract_raster_values_at_points.rs b/src/tools/gis_analysis/extract_raster_values_at_points.rs
index 10071f72e..7afc77a06 100644
--- a/src/tools/gis_analysis/extract_raster_values_at_points.rs
+++ b/src/tools/gis_analysis/extract_raster_values_at_points.rs
@@ -143,7 +143,7 @@ impl WhiteboxTool for ExtractRasterValuesAtPoints {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/gis_analysis/find_lowest_or_highest_points.rs b/src/tools/gis_analysis/find_lowest_or_highest_points.rs
index aa2550969..721436dad 100644
--- a/src/tools/gis_analysis/find_lowest_or_highest_points.rs
+++ b/src/tools/gis_analysis/find_lowest_or_highest_points.rs
@@ -146,7 +146,7 @@ impl WhiteboxTool for FindLowestOrHighestPoints {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/gis_analysis/find_patch_edge_cells.rs b/src/tools/gis_analysis/find_patch_edge_cells.rs
index 218307d1f..937e3ac64 100644
--- a/src/tools/gis_analysis/find_patch_edge_cells.rs
+++ b/src/tools/gis_analysis/find_patch_edge_cells.rs
@@ -127,7 +127,7 @@ impl WhiteboxTool for FindPatchOrClassEdgeCells {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/gis_analysis/highest_pos.rs b/src/tools/gis_analysis/highest_pos.rs
index 28936ea3a..070d12324 100644
--- a/src/tools/gis_analysis/highest_pos.rs
+++ b/src/tools/gis_analysis/highest_pos.rs
@@ -125,7 +125,7 @@ impl WhiteboxTool for HighestPosition {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/gis_analysis/hole_proportion.rs b/src/tools/gis_analysis/hole_proportion.rs
index 8abfb840a..92cf15fc2 100644
--- a/src/tools/gis_analysis/hole_proportion.rs
+++ b/src/tools/gis_analysis/hole_proportion.rs
@@ -122,7 +122,7 @@ impl WhiteboxTool for HoleProportion {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/gis_analysis/idw_interpolation.rs b/src/tools/gis_analysis/idw_interpolation.rs
index b5ddade03..161247fe7 100644
--- a/src/tools/gis_analysis/idw_interpolation.rs
+++ b/src/tools/gis_analysis/idw_interpolation.rs
@@ -2,7 +2,7 @@
This tool is part of the WhiteboxTools geospatial analysis library.
Authors: Dr. John Lindsay
Created: 10/05/2018
-Last Modified: 18/10/2019
+Last Modified: 9/12/2019
License: MIT
Most IDW tool have the option to work either based on a fixed number of neighbouring
@@ -205,7 +205,7 @@ impl WhiteboxTool for IdwInterpolation {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
@@ -240,7 +240,7 @@ impl WhiteboxTool for IdwInterpolation {
} else {
args[i + 1].to_string()
};
- } else if flag_val == "-cell_size" {
+ } else if flag_val == "-resolution" || flag_val == "-cell_size" {
grid_res = if keyval {
vec[1].to_string().parse::().unwrap()
} else {
@@ -518,9 +518,16 @@ impl WhiteboxTool for IdwInterpolation {
if !base_file.contains(&sep) && !base_file.contains("/") {
base_file = format!("{}{}", working_directory, base_file);
}
- let base = Raster::new(&base_file, "r")?;
+ let mut base = Raster::new(&base_file, "r")?;
+ base.configs.nodata = nodata;
Raster::initialize_using_file(&output_file, &base)
} else {
+ if grid_res == 0f64 {
+ return Err(Error::new(
+ ErrorKind::InvalidInput,
+ "The specified grid resolution is incorrect. Either a non-zero grid resolution \nor an input existing base file name must be used.",
+ ));
+ }
// base the output raster on the grid_res and the
// extent of the input vector.
let west: f64 = vector_data.header.x_min;
@@ -553,6 +560,8 @@ impl WhiteboxTool for IdwInterpolation {
let west = output.configs.west;
let north = output.configs.north;
output.configs.nodata = nodata; // in case a base image is used with a different nodata value.
+ let res_x = output.configs.resolution_x;
+ let res_y = output.configs.resolution_y;
// let kdtree = Arc::new(kdtree); // wrap FRS in an Arc
let frs = Arc::new(frs);
@@ -572,8 +581,8 @@ impl WhiteboxTool for IdwInterpolation {
for row in (0..rows).filter(|r| r % num_procs == tid) {
let mut data = vec![nodata; columns as usize];
for col in 0..columns {
- x = west + (col as f64 + 0.5) * grid_res;
- y = north - (row as f64 + 0.5) * grid_res;
+ x = west + (col as f64 + 0.5) * res_x;
+ y = north - (row as f64 + 0.5) * res_y;
let mut ret = frs.search(x, y);
if ret.len() < min_points {
ret = frs.knn_search(x, y, min_points);
diff --git a/src/tools/gis_analysis/intersect.rs b/src/tools/gis_analysis/intersect.rs
index 7a63e3b48..ced7130ce 100644
--- a/src/tools/gis_analysis/intersect.rs
+++ b/src/tools/gis_analysis/intersect.rs
@@ -183,7 +183,7 @@ impl WhiteboxTool for Intersect {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
@@ -308,7 +308,7 @@ impl WhiteboxTool for Intersect {
// place the points from both files into a KD-tree
let dimensions = 2;
let capacity_per_node = 64;
- let mut tree = KdTree::new_with_capacity(dimensions, capacity_per_node);
+ let mut tree = KdTree::with_capacity(dimensions, capacity_per_node);
let mut p: Point2D;
for record_num in 0..input.num_records {
let record = input.get_record(record_num);
@@ -374,7 +374,7 @@ impl WhiteboxTool for Intersect {
// place the points from both files into a KD-tree
let dimensions = 2;
let capacity_per_node = 64;
- let mut tree = KdTree::new_with_capacity(dimensions, capacity_per_node);
+ let mut tree = KdTree::with_capacity(dimensions, capacity_per_node);
let mut p: Point2D;
let mut total_points = 0;
for record_num in 0..input.num_records {
@@ -534,7 +534,7 @@ impl WhiteboxTool for Intersect {
// Break the polylines up into shorter lines at junction points.
let dimensions = 2;
let capacity_per_node = 64;
- let mut tree = KdTree::new_with_capacity(dimensions, capacity_per_node);
+ let mut tree = KdTree::with_capacity(dimensions, capacity_per_node);
let mut p: Point2D;
for i in 0..polylines.len() {
for j in 0..polylines[i].len() {
@@ -774,7 +774,7 @@ impl WhiteboxTool for Intersect {
let mut p: Point2D;
let dimensions = 2;
let capacity_per_node = 64;
- let mut tree = KdTree::new_with_capacity(dimensions, capacity_per_node);
+ let mut tree = KdTree::with_capacity(dimensions, capacity_per_node);
for i in 0..polygons.len() {
for j in 0..polygons[i].len() {
p = polygons[i][j];
@@ -979,8 +979,7 @@ impl WhiteboxTool for Intersect {
// now add the endpoints of each polyline into a kd tree
let dimensions = 2;
let capacity_per_node = 64;
- let mut kdtree =
- KdTree::new_with_capacity(dimensions, capacity_per_node);
+ let mut kdtree = KdTree::with_capacity(dimensions, capacity_per_node);
let mut p1: Point2D;
let mut p2: Point2D;
let mut p3: Point2D;
diff --git a/src/tools/gis_analysis/layer_footprint.rs b/src/tools/gis_analysis/layer_footprint.rs
index a46903648..f325e0136 100644
--- a/src/tools/gis_analysis/layer_footprint.rs
+++ b/src/tools/gis_analysis/layer_footprint.rs
@@ -144,7 +144,7 @@ impl WhiteboxTool for LayerFootprint {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/gis_analysis/line_intersections.rs b/src/tools/gis_analysis/line_intersections.rs
index 53572f956..5797757a9 100644
--- a/src/tools/gis_analysis/line_intersections.rs
+++ b/src/tools/gis_analysis/line_intersections.rs
@@ -155,7 +155,7 @@ impl WhiteboxTool for LineIntersections {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/gis_analysis/linearity_index.rs b/src/tools/gis_analysis/linearity_index.rs
index 05c03e0e1..d3b3560e9 100644
--- a/src/tools/gis_analysis/linearity_index.rs
+++ b/src/tools/gis_analysis/linearity_index.rs
@@ -129,7 +129,7 @@ impl WhiteboxTool for LinearityIndex {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/gis_analysis/lowest_pos.rs b/src/tools/gis_analysis/lowest_pos.rs
index 58b53c9db..e486c04ea 100644
--- a/src/tools/gis_analysis/lowest_pos.rs
+++ b/src/tools/gis_analysis/lowest_pos.rs
@@ -125,7 +125,7 @@ impl WhiteboxTool for LowestPosition {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/gis_analysis/max_abs_overlay.rs b/src/tools/gis_analysis/max_abs_overlay.rs
index 31edd0410..4a8c40fe6 100644
--- a/src/tools/gis_analysis/max_abs_overlay.rs
+++ b/src/tools/gis_analysis/max_abs_overlay.rs
@@ -120,7 +120,7 @@ impl WhiteboxTool for MaxAbsoluteOverlay {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/gis_analysis/max_overlay.rs b/src/tools/gis_analysis/max_overlay.rs
index e46848751..7cdcdef8b 100644
--- a/src/tools/gis_analysis/max_overlay.rs
+++ b/src/tools/gis_analysis/max_overlay.rs
@@ -121,7 +121,7 @@ impl WhiteboxTool for MaxOverlay {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/gis_analysis/medoid.rs b/src/tools/gis_analysis/medoid.rs
index 039c72fe0..2f9f4753c 100644
--- a/src/tools/gis_analysis/medoid.rs
+++ b/src/tools/gis_analysis/medoid.rs
@@ -144,7 +144,7 @@ impl WhiteboxTool for Medoid {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/gis_analysis/merge_line_segments.rs b/src/tools/gis_analysis/merge_line_segments.rs
index e5a30cb21..716fd8f39 100644
--- a/src/tools/gis_analysis/merge_line_segments.rs
+++ b/src/tools/gis_analysis/merge_line_segments.rs
@@ -149,7 +149,7 @@ impl WhiteboxTool for MergeLineSegments {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
@@ -269,7 +269,7 @@ impl WhiteboxTool for MergeLineSegments {
// Break the polylines up into shorter lines at junction points.
let dimensions = 2;
let capacity_per_node = 64;
- let mut tree = KdTree::new_with_capacity(dimensions, capacity_per_node);
+ let mut tree = KdTree::with_capacity(dimensions, capacity_per_node);
let mut p1: Point2D;
let mut p2: Point2D;
for i in 0..polylines.len() {
diff --git a/src/tools/gis_analysis/min_abs_overlay.rs b/src/tools/gis_analysis/min_abs_overlay.rs
index 874bcc97c..7d34ba086 100644
--- a/src/tools/gis_analysis/min_abs_overlay.rs
+++ b/src/tools/gis_analysis/min_abs_overlay.rs
@@ -120,7 +120,7 @@ impl WhiteboxTool for MinAbsoluteOverlay {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/gis_analysis/min_overlay.rs b/src/tools/gis_analysis/min_overlay.rs
index 7030ab90a..f99dd7cef 100644
--- a/src/tools/gis_analysis/min_overlay.rs
+++ b/src/tools/gis_analysis/min_overlay.rs
@@ -121,7 +121,7 @@ impl WhiteboxTool for MinOverlay {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/gis_analysis/minimum_bounding_box.rs b/src/tools/gis_analysis/minimum_bounding_box.rs
index bd9ce3a80..9fa090fd2 100644
--- a/src/tools/gis_analysis/minimum_bounding_box.rs
+++ b/src/tools/gis_analysis/minimum_bounding_box.rs
@@ -159,7 +159,7 @@ impl WhiteboxTool for MinimumBoundingBox {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/gis_analysis/minimum_bounding_circle.rs b/src/tools/gis_analysis/minimum_bounding_circle.rs
index b41a8d511..d9afd5443 100644
--- a/src/tools/gis_analysis/minimum_bounding_circle.rs
+++ b/src/tools/gis_analysis/minimum_bounding_circle.rs
@@ -146,7 +146,7 @@ impl WhiteboxTool for MinimumBoundingCircle {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/gis_analysis/minimum_bounding_envelope.rs b/src/tools/gis_analysis/minimum_bounding_envelope.rs
index da679e165..84f9b737f 100644
--- a/src/tools/gis_analysis/minimum_bounding_envelope.rs
+++ b/src/tools/gis_analysis/minimum_bounding_envelope.rs
@@ -148,7 +148,7 @@ impl WhiteboxTool for MinimumBoundingEnvelope {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/gis_analysis/minimum_convex_hull.rs b/src/tools/gis_analysis/minimum_convex_hull.rs
index 03371949f..33b930a84 100644
--- a/src/tools/gis_analysis/minimum_convex_hull.rs
+++ b/src/tools/gis_analysis/minimum_convex_hull.rs
@@ -147,7 +147,7 @@ impl WhiteboxTool for MinimumConvexHull {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/gis_analysis/mod.rs b/src/tools/gis_analysis/mod.rs
index f556343ac..291f42780 100644
--- a/src/tools/gis_analysis/mod.rs
+++ b/src/tools/gis_analysis/mod.rs
@@ -52,6 +52,7 @@ mod minimum_bounding_circle;
mod minimum_bounding_envelope;
mod minimum_convex_hull;
mod narrowness_index;
+mod natural_neighbour_interpolation;
mod nearest_neighbour_gridding;
mod patch_orientation;
mod percent_equal_to;
@@ -140,6 +141,7 @@ pub use self::minimum_bounding_circle::MinimumBoundingCircle;
pub use self::minimum_bounding_envelope::MinimumBoundingEnvelope;
pub use self::minimum_convex_hull::MinimumConvexHull;
pub use self::narrowness_index::NarrownessIndex;
+pub use self::natural_neighbour_interpolation::NaturalNeighbourInterpolation;
pub use self::nearest_neighbour_gridding::NearestNeighbourGridding;
pub use self::patch_orientation::PatchOrientation;
pub use self::percent_equal_to::PercentEqualTo;
diff --git a/src/tools/gis_analysis/narrowness_index.rs b/src/tools/gis_analysis/narrowness_index.rs
index c3a068902..1906180ab 100644
--- a/src/tools/gis_analysis/narrowness_index.rs
+++ b/src/tools/gis_analysis/narrowness_index.rs
@@ -132,7 +132,7 @@ impl WhiteboxTool for NarrownessIndex {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/gis_analysis/natural_neighbour_interpolation.rs b/src/tools/gis_analysis/natural_neighbour_interpolation.rs
new file mode 100644
index 000000000..41a6c9c47
--- /dev/null
+++ b/src/tools/gis_analysis/natural_neighbour_interpolation.rs
@@ -0,0 +1,727 @@
+/*
+This tool is part of the WhiteboxTools geospatial analysis library.
+Authors: Dr. John Lindsay
+Created: 08/12/2019
+Last Modified: 08/12/2019
+License: MIT
+*/
+
+use crate::algorithms::{point_in_poly, polygon_area, triangulate, Triangulation};
+use crate::raster::*;
+use crate::structures::{BoundingBox, Point2D};
+use crate::tools::*;
+use crate::vector::{FieldData, ShapeTypeDimension, ShapeType, Shapefile};
+use kdtree::distance::squared_euclidean;
+use kdtree::KdTree;
+use num_cpus;
+use std::collections::HashMap;
+use std::env;
+use std::f64;
+use std::io::{Error, ErrorKind};
+use std::path;
+use std::sync::mpsc;
+use std::sync::Arc;
+use std::thread;
+
+/// This tool can be used to interpolate a set of input vector points (`--input`) onto a raster grid using
+/// Sibson's (1981) natural neighbour method. Similar to inverse-distance-weight interpolation (`IdwInterpolation`),
+/// the natural neighbour method performs a weighted averaging of nearby point values to estimate the attribute
+/// (`--field`) value at grid cell intersections in the output raster (`--output`). However, the two methods differ
+/// quite significantly in the way that neighbours are identified and in the weighting scheme. First, natural neigbhour
+/// identifies neighbours to be used in the interpolation of a point by finding the points connected to the
+/// estimated value location in a [Delaunay triangulation](https://en.wikipedia.org/wiki/Delaunay_triangulation), that
+/// is, the so-called *natural neighbours*. This approach has the main advantage of not having to specify an arbitrary
+/// search distance or minimum number of nearest neighbours like many other interpolators do. Weights in the natural
+/// neighbour scheme are determined using an area-stealing approach, whereby the weight assigned to a neighbour's value
+/// is determined by the proportion of its [Voronoi polygon](https://en.wikipedia.org/wiki/Voronoi_diagram) that would
+/// be lost by inserting the interpolation point into the Voronoi diagram. That is, inserting the interpolation point into
+/// the Voronoi diagram results in the creation of a new polygon and shrinking the sizes of the Voronoi polygons associated
+/// with each of the natural neighbours. The larger the area by which a neighbours polygon is reduced through the
+/// insertion, relative to the polygon of the interpolation point, the greater the weight given to the neighbour point's
+/// value in the interpolation. Interpolation weights sum to one because the sum of the reduced polygon areas must
+/// account for the entire area of the interpolation points polygon.
+///
+/// The user must specify the attribute field containing point values (`--field`). Alternatively, if the input Shapefile
+/// contains z-values, the interpolation may be based on these values (`--use_z`). Either an output grid resolution
+/// (`--cell_size`) must be specified or alternatively an existing base file (`--base`) can be used to determine the
+/// output raster's (`--output`) resolution and spatial extent. Natural neighbour interpolation generally produces a
+/// satisfactorily smooth surface within the region of data points but can produce spurious breaks in the surface
+/// outside of this region. Thus, it is recommended that the output surface be clipped to the convex hull of the input
+/// points (`--clip`).
+///
+/// # Reference
+/// Sibson, R. (1981). "A brief description of natural neighbor interpolation (Chapter 2)". In V. Barnett (ed.).
+/// Interpolating Multivariate Data. Chichester: John Wiley. pp. 21–36.
+///
+/// # See Also
+/// `IdwInterpolation`, `NearestNeighbourGridding`
+pub struct NaturalNeighbourInterpolation {
+ name: String,
+ description: String,
+ toolbox: String,
+ parameters: Vec,
+ example_usage: String,
+}
+
+impl NaturalNeighbourInterpolation {
+ pub fn new() -> NaturalNeighbourInterpolation {
+ // public constructor
+ let name = "NaturalNeighbourInterpolation".to_string();
+ let toolbox = "GIS Analysis".to_string();
+ let description = "Creates a raster grid based on Sibson's natural neighbour method.".to_string();
+
+ let mut parameters = vec![];
+ parameters.push(ToolParameter {
+ name: "Input Vector Points File".to_owned(),
+ flags: vec!["-i".to_owned(), "--input".to_owned()],
+ description: "Input vector points file.".to_owned(),
+ parameter_type: ParameterType::ExistingFile(ParameterFileType::Vector(
+ VectorGeometryType::Point,
+ )),
+ default_value: None,
+ optional: false,
+ });
+
+ parameters.push(ToolParameter {
+ name: "Field Name".to_owned(),
+ flags: vec!["--field".to_owned()],
+ description: "Input field name in attribute table.".to_owned(),
+ parameter_type: ParameterType::VectorAttributeField(
+ AttributeType::Number,
+ "--input".to_string(),
+ ),
+ default_value: None,
+ optional: true,
+ });
+
+ parameters.push(ToolParameter {
+ name: "Use Shapefile 'z' values?".to_owned(),
+ flags: vec!["--use_z".to_owned()],
+ description:
+ "Use the 'z' dimension of the Shapefile's geometry instead of an attribute field?"
+ .to_owned(),
+ parameter_type: ParameterType::Boolean,
+ default_value: Some("false".to_string()),
+ optional: true,
+ });
+
+ parameters.push(ToolParameter {
+ name: "Output Raster File".to_owned(),
+ flags: vec!["-o".to_owned(), "--output".to_owned()],
+ description: "Output raster file.".to_owned(),
+ parameter_type: ParameterType::NewFile(ParameterFileType::Raster),
+ default_value: None,
+ optional: false,
+ });
+
+ parameters.push(ToolParameter{
+ name: "Cell Size (optional)".to_owned(),
+ flags: vec!["--cell_size".to_owned()],
+ description: "Optionally specified cell size of output raster. Not used when base raster is specified.".to_owned(),
+ parameter_type: ParameterType::Float,
+ default_value: None,
+ optional: true
+ });
+
+ parameters.push(ToolParameter{
+ name: "Base Raster File (optional)".to_owned(),
+ flags: vec!["--base".to_owned()],
+ description: "Optionally specified input base raster file. Not used when a cell size is specified.".to_owned(),
+ parameter_type: ParameterType::ExistingFile(ParameterFileType::Raster),
+ default_value: None,
+ optional: true
+ });
+
+ parameters.push(ToolParameter {
+ name: "Clip to convex hull?".to_owned(),
+ flags: vec!["--clip".to_owned()],
+ description: "Clip the data to the convex hull of the points?".to_owned(),
+ parameter_type: ParameterType::Boolean,
+ default_value: Some("true".to_string()),
+ optional: true,
+ });
+
+ let sep: String = path::MAIN_SEPARATOR.to_string();
+ let p = format!("{}", env::current_dir().unwrap().display());
+ let e = format!("{}", env::current_exe().unwrap().display());
+ let mut short_exe = e
+ .replace(&p, "")
+ .replace(".exe", "")
+ .replace(".", "")
+ .replace(&sep, "");
+ if e.contains(".exe") {
+ short_exe += ".exe";
+ }
+ let usage = format!(
+ ">>.*{0} -r={1} -v --wd=\"*path*to*data*\" -i=points.shp --field=HEIGHT -o=surface.tif --resolution=10.0 --clip",
+ short_exe, name
+ ).replace("*", &sep);
+
+ NaturalNeighbourInterpolation {
+ name: name,
+ description: description,
+ toolbox: toolbox,
+ parameters: parameters,
+ example_usage: usage,
+ }
+ }
+}
+
+impl WhiteboxTool for NaturalNeighbourInterpolation {
+ fn get_source_file(&self) -> String {
+ String::from(file!())
+ }
+
+ fn get_tool_name(&self) -> String {
+ self.name.clone()
+ }
+
+ fn get_tool_description(&self) -> String {
+ self.description.clone()
+ }
+
+ fn get_tool_parameters(&self) -> String {
+ let mut s = String::from("{\"parameters\": [");
+ for i in 0..self.parameters.len() {
+ if i < self.parameters.len() - 1 {
+ s.push_str(&(self.parameters[i].to_string()));
+ s.push_str(",");
+ } else {
+ s.push_str(&(self.parameters[i].to_string()));
+ }
+ }
+ s.push_str("]}");
+ s
+ }
+
+ fn get_example_usage(&self) -> String {
+ self.example_usage.clone()
+ }
+
+ fn get_toolbox(&self) -> String {
+ self.toolbox.clone()
+ }
+
+ fn run<'a>(
+ &self,
+ args: Vec,
+ working_directory: &'a str,
+ verbose: bool,
+ ) -> Result<(), Error> {
+ let mut input_file: String = "".to_string();
+ let mut field_name = String::new();
+ let mut use_z = false;
+ let mut use_field = false;
+ let mut output_file: String = "".to_string();
+ let mut grid_res: f64 = 0.0;
+ let mut base_file = String::new();
+ let mut clip_to_hull = false;
+
+ // read the arguments
+ if args.len() == 0 {
+ return Err(Error::new(
+ ErrorKind::InvalidInput,
+ "Tool run with no parameters.",
+ ));
+ }
+ for i in 0..args.len() {
+ let mut arg = args[i].replace("\"", "");
+ arg = arg.replace("\'", "");
+ let cmd = arg.split("="); // in case an equals sign was used
+ let vec = cmd.collect::>();
+ let mut keyval = false;
+ if vec.len() > 1 {
+ keyval = true;
+ }
+ let flag_val = vec[0].to_lowercase().replace("--", "-");
+ if flag_val == "-i" || flag_val == "-input" {
+ input_file = if keyval {
+ vec[1].to_string()
+ } else {
+ args[i + 1].to_string()
+ };
+ } else if flag_val == "-field" {
+ field_name = if keyval {
+ vec[1].to_string()
+ } else {
+ args[i + 1].to_string()
+ };
+ use_field = true;
+ } else if flag_val.contains("use_z") {
+ if vec.len() == 1 || !vec[1].to_string().to_lowercase().contains("false") {
+ use_z = true;
+ }
+ } else if flag_val == "-o" || flag_val == "-output" {
+ output_file = if keyval {
+ vec[1].to_string()
+ } else {
+ args[i + 1].to_string()
+ };
+ } else if flag_val == "-resolution" || flag_val == "-cell_size" {
+ grid_res = if keyval {
+ vec[1].to_string().parse::().unwrap()
+ } else {
+ args[i + 1].to_string().parse::().unwrap()
+ };
+ } else if flag_val.contains("clip") {
+ if vec.len() == 1 || !vec[1].to_string().to_lowercase().contains("false") {
+ clip_to_hull = true;
+ }
+ } else if flag_val == "-base" {
+ base_file = if keyval {
+ vec[1].to_string()
+ } else {
+ args[i + 1].to_string()
+ };
+ }
+ }
+
+ let sep: String = path::MAIN_SEPARATOR.to_string();
+ let mut progress: usize;
+ let mut old_progress: usize = 1;
+
+ let start = Instant::now();
+
+ if verbose {
+ println!("***************{}", "*".repeat(self.get_tool_name().len()));
+ println!("* Welcome to {} *", self.get_tool_name());
+ println!("***************{}", "*".repeat(self.get_tool_name().len()));
+ }
+
+ if !input_file.contains(path::MAIN_SEPARATOR) && !input_file.contains("/") {
+ input_file = format!("{}{}", working_directory, input_file);
+ }
+
+ if !output_file.contains(&sep) && !output_file.contains("/") {
+ output_file = format!("{}{}", working_directory, output_file);
+ }
+
+ let input = Shapefile::read(&input_file)?;
+
+ // make sure the input vector file is of points type
+ if input.header.shape_type.base_shape_type() != ShapeType::Point
+ && input.header.shape_type.base_shape_type() != ShapeType::MultiPoint
+ {
+ return Err(Error::new(
+ ErrorKind::InvalidInput,
+ "The input vector data must be of POINT base shape type.",
+ ));
+ }
+
+ if !use_z && !use_field {
+ return Err(Error::new(
+ ErrorKind::InvalidInput,
+ "If vector data 'Z' data are unavailable (--use_z), an attribute field must be specified (--field=).",
+ ));
+ }
+
+ if use_z && input.header.shape_type.dimension() != ShapeTypeDimension::Z {
+ return Err(Error::new(
+ ErrorKind::InvalidInput,
+ "The input vector data must be of 'POINTZ' or 'MULTIPOINTZ' ShapeType to use the --use_z flag.",
+ ));
+ } else if use_field {
+ // What is the index of the field to be analyzed?
+ let field_index = match input.attributes.get_field_num(&field_name) {
+ Some(i) => i,
+ None => {
+ return Err(Error::new(
+ ErrorKind::InvalidInput,
+ "The specified field name does not exist in input shapefile.",
+ ))
+ }
+ };
+
+ // Is the field numeric?
+ if !input.attributes.is_field_numeric(field_index) {
+ return Err(Error::new(
+ ErrorKind::InvalidInput,
+ "The specified attribute field is non-numeric.",
+ ));
+ }
+ }
+
+ let nodata = -32768.0f64;
+ let mut output = if !base_file.trim().is_empty() || grid_res == 0f64 {
+ if !base_file.contains(&sep) && !base_file.contains("/") {
+ base_file = format!("{}{}", working_directory, base_file);
+ }
+ let mut base = Raster::new(&base_file, "r")?;
+ base.configs.nodata = nodata;
+ Raster::initialize_using_file(&output_file, &base)
+ } else {
+ if grid_res == 0f64 {
+ return Err(Error::new(
+ ErrorKind::InvalidInput,
+ "The specified grid resolution is incorrect. Either a non-zero grid resolution \nor an input existing base file name must be used.",
+ ));
+ }
+ // base the output raster on the grid_res and the
+ // extent of the input vector.
+ let west: f64 = input.header.x_min;
+ let north: f64 = input.header.y_max;
+ let rows: isize = (((north - input.header.y_min) / grid_res).ceil()) as isize;
+ let columns: isize = (((input.header.x_max - west) / grid_res).ceil()) as isize;
+ let south: f64 = north - rows as f64 * grid_res;
+ let east = west + columns as f64 * grid_res;
+
+ let mut configs = RasterConfigs {
+ ..Default::default()
+ };
+ configs.rows = rows as usize;
+ configs.columns = columns as usize;
+ configs.north = north;
+ configs.south = south;
+ configs.east = east;
+ configs.west = west;
+ configs.resolution_x = grid_res;
+ configs.resolution_y = grid_res;
+ configs.nodata = nodata;
+ configs.data_type = DataType::F32;
+ configs.photometric_interp = PhotometricInterpretation::Continuous;
+
+ Raster::initialize_using_config(&output_file, &configs)
+ };
+
+ let rows = output.configs.rows as isize;
+ let columns = output.configs.columns as isize;
+ let west = output.configs.west;
+ let north = output.configs.north;
+ output.configs.nodata = nodata; // in case a base image is used with a different nodata value.
+ output.configs.palette = "spectrum.pal".to_string();
+ output.configs.data_type = DataType::F32;
+ output.configs.photometric_interp = PhotometricInterpretation::Continuous;
+
+ let mut points: Vec = Vec::with_capacity(input.get_total_num_points());
+ let mut z_values: Vec = Vec::with_capacity(input.get_total_num_points());
+
+ const DIMENSIONS: usize = 2;
+ const CAPACITY_PER_NODE: usize = 64;
+ let mut tree = KdTree::with_capacity(DIMENSIONS, CAPACITY_PER_NODE);
+ let mut p = 0;
+ for record_num in 0..input.num_records {
+ let record = input.get_record(record_num);
+ for i in 0..record.num_points as usize {
+ points.push(Point2D::new(record.points[i].x, record.points[i].y));
+ if use_z {
+ z_values.push(record.z_array[i]);
+ } else if use_field {
+ match input.attributes.get_value(record_num, &field_name) {
+ FieldData::Int(val) => {
+ z_values.push(val as f64);
+ }
+ FieldData::Real(val) => {
+ z_values.push(val);
+ }
+ _ => {
+ // likely a null field
+ z_values.push(0f64);
+ }
+ }
+ }
+ tree.add([points[p].x, points[p].y], p).unwrap();
+ p += 1;
+ }
+
+ if verbose {
+ progress =
+ (100.0_f64 * (record_num + 1) as f64 / input.num_records as f64) as usize;
+ if progress != old_progress {
+ println!("Reading points: {}%", progress);
+ old_progress = progress;
+ }
+ }
+ }
+
+ if verbose {
+ println!("Performing triangulation...");
+ }
+ // this is where the heavy-lifting is
+ let delaunay = triangulate(&points).expect("No triangulation exists.");
+
+ // get the hull
+ let dont_clip_to_hull = !clip_to_hull;
+ let mut hull_vertices: Vec = vec![points[delaunay.hull[0]].clone()];
+ for a in (0..delaunay.hull.len()).rev() {
+ hull_vertices.push(points[delaunay.hull[a]].clone());
+ }
+
+ if verbose {
+ println!("Creating point-halfedge mapping...");
+ }
+ const EMPTY: usize = usize::max_value();
+ let mut point_edge_map = HashMap::new(); // point id to half-edge id
+ for edge in 0..delaunay.triangles.len() {
+ let endpoint = delaunay.triangles[delaunay.next_halfedge(edge)];
+ if !point_edge_map.contains_key(&endpoint) || delaunay.halfedges[edge] == EMPTY {
+ point_edge_map.insert(endpoint, edge);
+ }
+ if verbose {
+ progress =
+ (100.0_f64 * edge as f64 / (delaunay.triangles.len() - 1) as f64) as usize;
+ if progress != old_progress {
+ println!("Progress: {}%", progress);
+ old_progress = progress;
+ }
+ }
+ }
+
+ let res_x = output.configs.resolution_x;
+ let res_y = output.configs.resolution_y;
+
+ if verbose {
+ println!("Interpolating...");
+ }
+ let points = Arc::new(points);
+ let z_values = Arc::new(z_values);
+ let delaunay = Arc::new(delaunay);
+ let tree = Arc::new(tree);
+ let hull_vertices = Arc::new(hull_vertices);
+ let point_edge_map = Arc::new(point_edge_map);
+ let num_procs = num_cpus::get() as isize;
+ let (tx, rx) = mpsc::channel();
+ for tid in 0..num_procs {
+ let points = points.clone();
+ let z_values = z_values.clone();
+ let delaunay = delaunay.clone();
+ let tree = tree.clone();
+ let hull_vertices = hull_vertices.clone();
+ let point_edge_map = point_edge_map.clone();
+ let tx = tx.clone();
+ thread::spawn(move || {
+
+ let (mut px, mut py): (f64, f64);
+ let mut previous_nn = EMPTY;
+ let mut delaunay2: Triangulation;
+ let mut natural_neighbours: Vec = vec![];
+ let mut nn_points: Vec = vec![];
+ let mut num_neighbours = 0;
+ let mut areas1: Vec = vec![];
+ let mut areas2: Vec;
+ let mut edge: usize;
+ let mut edges: Vec;
+ let mut point_num: usize;
+ let mut vertices: Vec;
+ let mut triangles: Vec;
+ let mut ghost_box: BoundingBox;
+ let mut expansion: f64;
+ let mut gap: f64;
+ let mut num_edge_points: usize;
+ let mut endpoint: usize;
+ let mut sum_diff: f64;
+ let mut z: f64;
+ for row in (0..rows).filter(|r| r % num_procs == tid) {
+ let mut data = vec![nodata; columns as usize];
+ for col in 0..columns {
+ px = west + (col as f64 + 0.5) * res_x;
+ py = north - (row as f64 + 0.5) * res_y;
+ if dont_clip_to_hull || point_in_poly(&Point2D::new(px, py), &hull_vertices) {
+ // find the nearest point
+ match tree.nearest(&[px, py], 1, &squared_euclidean) {
+ Ok(ret) => {
+ point_num = *ret[0].1;
+
+ if point_num != previous_nn {
+
+ // get the edge that is incoming to 'point_num'
+ edge = match point_edge_map.get(&point_num) {
+ Some(e) => *e,
+ None => EMPTY,
+ };
+ if edge != EMPTY {
+
+ // find all the neighbours of point_num and their neighbours too
+ natural_neighbours = delaunay.natural_neighbours_2nd_order(edge);
+ num_neighbours = natural_neighbours.len();
+
+ nn_points = natural_neighbours
+ .clone()
+ .into_iter()
+ .map(|p| points[p].clone())
+ .collect();
+
+ /////////////////////////////////////////////
+ // Create the Voronoi diagram of the points
+ /////////////////////////////////////////////
+
+ // Add a frame of hidden points surrounding the data, to serve as an artificial hull.
+ ghost_box = BoundingBox::from_points(&nn_points);
+
+ // expand the box by a factor of the average point spacing.
+ expansion = ((ghost_box.max_x - ghost_box.min_x)
+ * (ghost_box.max_y - ghost_box.min_y)
+ / num_neighbours as f64)
+ .sqrt();
+ ghost_box.expand_by(2.0 * expansion);
+
+ gap = expansion / 2f64; // One-half the average point spacing
+ num_edge_points = ((ghost_box.max_x - ghost_box.min_x) / gap) as usize;
+ for x in 0..num_edge_points {
+ nn_points.push(Point2D::new(
+ ghost_box.min_x + x as f64 * gap,
+ ghost_box.min_y,
+ ));
+ nn_points.push(Point2D::new(
+ ghost_box.min_x + x as f64 * gap,
+ ghost_box.max_y,
+ ));
+ }
+
+ num_edge_points = ((ghost_box.max_y - ghost_box.min_y) / gap) as usize;
+ for y in 0..num_edge_points {
+ nn_points.push(Point2D::new(
+ ghost_box.min_x,
+ ghost_box.min_y + y as f64 * gap,
+ ));
+ nn_points.push(Point2D::new(
+ ghost_box.max_x,
+ ghost_box.min_y + y as f64 * gap,
+ ));
+ }
+
+ delaunay2 = triangulate(&nn_points).expect("No triangulation exists.");
+
+
+ // measure their areas
+ areas1 = vec![0f64; num_neighbours];
+ let mut point_edge_map2 = HashMap::new(); // point id to half-edge id
+ for edge in 0..delaunay2.triangles.len() {
+ endpoint = delaunay2.triangles[delaunay2.next_halfedge(edge)];
+ if !point_edge_map2.contains_key(&endpoint) || delaunay2.halfedges[edge] == EMPTY {
+ point_edge_map2.insert(endpoint, edge);
+ }
+ }
+ for a in 0..num_neighbours {
+ edge = match point_edge_map2.get(&a) {
+ Some(e) => *e,
+ None => EMPTY,
+ };
+ if edge != EMPTY {
+ edges = delaunay2.edges_around_point(edge);
+ triangles = edges
+ .into_iter()
+ .map(|e| delaunay2.triangle_of_edge(e))
+ .collect();
+
+ vertices = triangles
+ .into_iter()
+ .map(|t| delaunay2.triangle_center(&nn_points, t))
+ .collect();
+
+ areas1[a] = polygon_area(&vertices);
+ }
+ }
+
+ previous_nn = point_num;
+ }
+ }
+
+ if areas1.len() > 0 {
+ // now add the grid cell centre point in and re-triangulate.
+ nn_points.pop();
+ nn_points.push(Point2D::new(px, py));
+ let delaunay3 = triangulate(&nn_points).expect("No triangulation exists.");
+ let mut point_edge_map2 = HashMap::new(); // point id to half-edge id
+ for edge in 0..delaunay3.triangles.len() {
+ endpoint = delaunay3.triangles[delaunay3.next_halfedge(edge)];
+ if !point_edge_map2.contains_key(&endpoint) || delaunay3.halfedges[edge] == EMPTY {
+ point_edge_map2.insert(endpoint, edge);
+ }
+ }
+ areas2 = vec![0f64; num_neighbours];
+ for a in 0..num_neighbours {
+ edge = match point_edge_map2.get(&a) {
+ Some(e) => *e,
+ None => EMPTY,
+ };
+ if edge != EMPTY {
+ edges = delaunay3.edges_around_point(edge);
+ triangles = edges
+ .into_iter()
+ .map(|e| delaunay3.triangle_of_edge(e))
+ .collect();
+
+ vertices= triangles
+ .into_iter()
+ .map(|t| delaunay3.triangle_center(&nn_points, t))
+ .collect();
+
+ areas2[a] = polygon_area(&vertices);
+ }
+ }
+
+ sum_diff = 0f64;
+ for a in 0..num_neighbours {
+ sum_diff += areas1[a] - areas2[a];
+ }
+ if sum_diff > 0f64 {
+ z = 0f64;
+ for a in 0..num_neighbours {
+ z += (areas1[a] - areas2[a]) / sum_diff * z_values[natural_neighbours[a]];
+ }
+ data[col as usize] = z;
+ }
+ }
+
+ },
+ Err(_) => {
+ // no point found; output nodata
+ }
+ };
+ }
+ }
+ tx.send((row, data)).unwrap();
+ }
+ });
+ }
+
+ for row in 0..rows {
+ let data = rx.recv().unwrap();
+ output.set_row_data(data.0, data.1);
+ if verbose {
+ progress = (100.0_f64 * row as f64 / (rows - 1) as f64) as usize;
+ if progress != old_progress {
+ println!("Interpolating: {}%", progress);
+ old_progress = progress;
+ }
+ }
+ }
+
+ let elapsed_time = get_formatted_elapsed_time(start);
+
+ output.add_metadata_entry(format!(
+ "Created by whitebox_tools\' {} tool",
+ self.get_tool_name()
+ ));
+ output.add_metadata_entry(format!("Input file: {}", input_file));
+ output.add_metadata_entry(format!("Field name: {}", field_name));
+ output.add_metadata_entry(format!("Use z-field: {}", use_z));
+ if grid_res > 0f64 {
+ output.add_metadata_entry(format!("Grid resolution: {}", grid_res));
+ } else {
+ output.add_metadata_entry(format!("Base file: {}", base_file));
+ }
+ output.add_metadata_entry(format!("Clip to hull: {}", clip_to_hull));
+ output.add_metadata_entry(format!("Elapsed Time (including I/O): {}", elapsed_time));
+
+ if verbose {
+ println!("Saving data...")
+ };
+ let _ = match output.write() {
+ Ok(_) => {
+ if verbose {
+ println!("Output file written")
+ }
+ }
+ Err(e) => return Err(e),
+ };
+
+ let elapsed_time = get_formatted_elapsed_time(start);
+
+ if verbose {
+ println!("{}", &format!("Elapsed Time: {}", elapsed_time));
+ }
+
+ Ok(())
+ }
+}
diff --git a/src/tools/gis_analysis/nearest_neighbour_gridding.rs b/src/tools/gis_analysis/nearest_neighbour_gridding.rs
index bbf419dde..41883bb98 100644
--- a/src/tools/gis_analysis/nearest_neighbour_gridding.rs
+++ b/src/tools/gis_analysis/nearest_neighbour_gridding.rs
@@ -2,7 +2,7 @@
This tool is part of the WhiteboxTools geospatial analysis library.
Authors: Dr. John Lindsay
Created: 09/10/2018
-Last Modified: 18/10/2019
+Last Modified: 09/12/2019
License: MIT
*/
@@ -173,7 +173,7 @@ impl WhiteboxTool for NearestNeighbourGridding {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
@@ -373,7 +373,8 @@ impl WhiteboxTool for NearestNeighbourGridding {
if !base_file.contains(&sep) && !base_file.contains("/") {
base_file = format!("{}{}", working_directory, base_file);
}
- let base = Raster::new(&base_file, "r")?;
+ let mut base = Raster::new(&base_file, "r")?;
+ base.configs.nodata = nodata;
Raster::initialize_using_file(&output_file, &base)
} else {
// base the output raster on the grid_res and the
@@ -408,6 +409,8 @@ impl WhiteboxTool for NearestNeighbourGridding {
let west = output.configs.west;
let north = output.configs.north;
output.configs.nodata = nodata; // in case a base image is used with a different nodata value.
+ let res_x = output.configs.resolution_x;
+ let res_y = output.configs.resolution_y;
let frs = Arc::new(frs);
let num_procs = num_cpus::get() as isize;
@@ -420,8 +423,8 @@ impl WhiteboxTool for NearestNeighbourGridding {
for row in (0..rows).filter(|r| r % num_procs == tid) {
let mut data = vec![nodata; columns as usize];
for col in 0..columns {
- x = west + (col as f64 + 0.5) * grid_res;
- y = north - (row as f64 + 0.5) * grid_res;
+ x = west + (col as f64 + 0.5) * res_x;
+ y = north - (row as f64 + 0.5) * res_y;
let ret = frs.knn_search(x, y, 1);
if ret.len() == 1 {
if ret[0].1 <= max_dist {
diff --git a/src/tools/gis_analysis/patch_orientation.rs b/src/tools/gis_analysis/patch_orientation.rs
index 35c4f7bd8..44a84dadd 100644
--- a/src/tools/gis_analysis/patch_orientation.rs
+++ b/src/tools/gis_analysis/patch_orientation.rs
@@ -127,7 +127,7 @@ impl WhiteboxTool for PatchOrientation {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/gis_analysis/percent_equal_to.rs b/src/tools/gis_analysis/percent_equal_to.rs
index 5b2a48389..04596972e 100644
--- a/src/tools/gis_analysis/percent_equal_to.rs
+++ b/src/tools/gis_analysis/percent_equal_to.rs
@@ -134,7 +134,7 @@ impl WhiteboxTool for PercentEqualTo {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/gis_analysis/percent_greater_than.rs b/src/tools/gis_analysis/percent_greater_than.rs
index cb5234bfc..e1441c26e 100644
--- a/src/tools/gis_analysis/percent_greater_than.rs
+++ b/src/tools/gis_analysis/percent_greater_than.rs
@@ -134,7 +134,7 @@ impl WhiteboxTool for PercentGreaterThan {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/gis_analysis/percent_less_than.rs b/src/tools/gis_analysis/percent_less_than.rs
index e932e8116..e93248135 100644
--- a/src/tools/gis_analysis/percent_less_than.rs
+++ b/src/tools/gis_analysis/percent_less_than.rs
@@ -134,7 +134,7 @@ impl WhiteboxTool for PercentLessThan {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/gis_analysis/perimeter_area_ratio.rs b/src/tools/gis_analysis/perimeter_area_ratio.rs
index 4482e648e..241f0990f 100644
--- a/src/tools/gis_analysis/perimeter_area_ratio.rs
+++ b/src/tools/gis_analysis/perimeter_area_ratio.rs
@@ -124,7 +124,7 @@ impl WhiteboxTool for PerimeterAreaRatio {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/gis_analysis/pick_from_list.rs b/src/tools/gis_analysis/pick_from_list.rs
index 5846df88d..e26cea43f 100644
--- a/src/tools/gis_analysis/pick_from_list.rs
+++ b/src/tools/gis_analysis/pick_from_list.rs
@@ -133,7 +133,7 @@ impl WhiteboxTool for PickFromList {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/gis_analysis/polygon_area.rs b/src/tools/gis_analysis/polygon_area.rs
index 29a44ea8e..19bf708bf 100644
--- a/src/tools/gis_analysis/polygon_area.rs
+++ b/src/tools/gis_analysis/polygon_area.rs
@@ -123,7 +123,7 @@ impl WhiteboxTool for PolygonArea {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/gis_analysis/polygon_long_axis.rs b/src/tools/gis_analysis/polygon_long_axis.rs
index 0b0ab7e50..fe628f357 100644
--- a/src/tools/gis_analysis/polygon_long_axis.rs
+++ b/src/tools/gis_analysis/polygon_long_axis.rs
@@ -135,7 +135,7 @@ impl WhiteboxTool for PolygonLongAxis {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/gis_analysis/polygon_perimeter.rs b/src/tools/gis_analysis/polygon_perimeter.rs
index 46942414d..e2aa2f739 100644
--- a/src/tools/gis_analysis/polygon_perimeter.rs
+++ b/src/tools/gis_analysis/polygon_perimeter.rs
@@ -118,7 +118,7 @@ impl WhiteboxTool for PolygonPerimeter {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/gis_analysis/polygon_short_axis.rs b/src/tools/gis_analysis/polygon_short_axis.rs
index 69496dc69..fce8934d9 100644
--- a/src/tools/gis_analysis/polygon_short_axis.rs
+++ b/src/tools/gis_analysis/polygon_short_axis.rs
@@ -135,7 +135,7 @@ impl WhiteboxTool for PolygonShortAxis {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/gis_analysis/polygonize.rs b/src/tools/gis_analysis/polygonize.rs
index 91ca2f02d..bdb73d362 100644
--- a/src/tools/gis_analysis/polygonize.rs
+++ b/src/tools/gis_analysis/polygonize.rs
@@ -146,7 +146,7 @@ impl WhiteboxTool for Polygonize {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
@@ -331,7 +331,7 @@ impl WhiteboxTool for Polygonize {
// now add the endpoints of each polyline into a kd tree
let dimensions = 2;
let capacity_per_node = 64;
- let mut kdtree = KdTree::new_with_capacity(dimensions, capacity_per_node);
+ let mut kdtree = KdTree::with_capacity(dimensions, capacity_per_node);
let mut p1: Point2D;
let mut p2: Point2D;
let mut p3: Point2D;
diff --git a/src/tools/gis_analysis/radius_of_gyration.rs b/src/tools/gis_analysis/radius_of_gyration.rs
index a28584db9..dcb9fee90 100644
--- a/src/tools/gis_analysis/radius_of_gyration.rs
+++ b/src/tools/gis_analysis/radius_of_gyration.rs
@@ -139,7 +139,7 @@ impl WhiteboxTool for RadiusOfGyration {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/gis_analysis/raster_area.rs b/src/tools/gis_analysis/raster_area.rs
index f146e9745..db5a10de1 100644
--- a/src/tools/gis_analysis/raster_area.rs
+++ b/src/tools/gis_analysis/raster_area.rs
@@ -2,7 +2,7 @@
This tool is part of the WhiteboxTools geospatial analysis library.
Authors: Dr. John Lindsay
Created: 10/02/2019
-Last Modified: 18/10/2019
+Last Modified: 04/12/2019
License: MIT
*/
@@ -169,7 +169,7 @@ impl WhiteboxTool for RasterArea {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
@@ -354,10 +354,10 @@ impl WhiteboxTool for RasterArea {
}
}
}
- } else {
+ } else { // map units
let is_geographic = input.is_in_geographic_coordinates();
if is_geographic && verbose {
- println!("Warning: the input file does not appear to be in a projected coodinate system. Area values will only be estimates.");
+ println!("Warning: the input file does not appear to be in a projected coordinate system. Area values will only be estimates.");
}
let num_procs = num_cpus::get() as isize;
@@ -368,6 +368,7 @@ impl WhiteboxTool for RasterArea {
thread::spawn(move || {
let mut resx = input.configs.resolution_x;
let mut resy = input.configs.resolution_y;
+ let mut cell_area = resx * resy;
let mut area_data = vec![0f64; num_bins];
let mut val: f64;
let mut bin: usize;
@@ -377,12 +378,13 @@ impl WhiteboxTool for RasterArea {
mid_lat = input.get_y_from_row(row).to_radians();
resx = resx * 111_111.0 * mid_lat.cos();
resy = resy * 111_111.0;
+ cell_area = resx * resy;
}
for col in 0..columns {
val = input.get_value(row, col);
if val != nodata && val != back_val && val >= min_val && val <= max_val {
bin = (val - min_val).floor() as usize;
- area_data[bin] += resx * resy;
+ area_data[bin] += cell_area;
}
}
}
@@ -417,7 +419,7 @@ impl WhiteboxTool for RasterArea {
output.reinitialize_values(out_nodata);
output.configs.nodata = out_nodata;
output.configs.photometric_interp = PhotometricInterpretation::Continuous;
- output.configs.data_type = DataType::I32;
+ output.configs.data_type = DataType::F32;
for row in 0..rows {
for col in 0..columns {
val = input.get_value(row, col);
diff --git a/src/tools/gis_analysis/raster_cell_assignment.rs b/src/tools/gis_analysis/raster_cell_assignment.rs
index 884ea69f2..24cecdb4a 100644
--- a/src/tools/gis_analysis/raster_cell_assignment.rs
+++ b/src/tools/gis_analysis/raster_cell_assignment.rs
@@ -132,7 +132,7 @@ impl WhiteboxTool for RasterCellAssignment {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
diff --git a/src/tools/gis_analysis/raster_perimeter.rs b/src/tools/gis_analysis/raster_perimeter.rs
new file mode 100644
index 000000000..e191ce7d3
--- /dev/null
+++ b/src/tools/gis_analysis/raster_perimeter.rs
@@ -0,0 +1,526 @@
+/*
+This tool is part of the WhiteboxTools geospatial analysis library.
+Authors: Dr. John Lindsay
+Created: 10/02/2019
+Last Modified: 04/12/2019
+License: MIT
+*/
+
+use crate::raster::*;
+use crate::tools::*;
+use num_cpus;
+use std::env;
+use std::f64;
+use std::io::{Error, ErrorKind};
+use std::path;
+use std::sync::mpsc;
+use std::sync::Arc;
+use std::thread;
+
+/// This tools estimates the area of each category, polygon, or patch in an input raster. The input raster must be categorical
+/// in data scale. Rasters with floating-point cell values are not good candidates for an area analysis. The user must specify
+/// whether the output is given in `grid cells` or `map units` (`--units`). Map Units are physical units, e.g. if the rasters's
+/// scale is in metres, areas will report in square-metres. Notice that square-metres can be converted into hectares by dividing
+/// by 10,000 and into square-kilometres by dividing by 1,000,000. If the input raster is in geographic coordinates (i.e.
+/// latitude and longitude) a warning will be issued and areas will be estimated based on per-row calculated degree lengths.
+///
+/// The tool can be run with a raster output (`--output`), a text output (`--out_text`), or both. If niether outputs are specified,
+/// the tool will automatically output a raster named `area.tif`.
+///
+/// Zero values in the input raster may be excluded from the area analysis if the `--zero_back` flag is used.
+///
+/// To calculate the area of vector polygons, use the `PolygonArea` tool instead.
+///
+/// # See Also
+/// `RasterArea`
+pub struct RasterPerimeter {
+ name: String,
+ description: String,
+ toolbox: String,
+ parameters: Vec,
+ example_usage: String,
+}
+
+impl RasterPerimeter {
+ pub fn new() -> RasterPerimeter {
+ // public constructor
+ let name = "RasterPerimeter".to_string();
+ let toolbox = "GIS Analysis".to_string();
+ let description =
+ "Calculates the perimeters of polygons or classes within a raster image."
+ .to_string();
+
+ let mut parameters = vec![];
+ parameters.push(ToolParameter {
+ name: "Input File".to_owned(),
+ flags: vec!["-i".to_owned(), "--input".to_owned()],
+ description: "Input raster file.".to_owned(),
+ parameter_type: ParameterType::ExistingFile(ParameterFileType::Raster),
+ default_value: None,
+ optional: false,
+ });
+
+ parameters.push(ToolParameter {
+ name: "Output File".to_owned(),
+ flags: vec!["-o".to_owned(), "--output".to_owned()],
+ description: "Output raster file.".to_owned(),
+ parameter_type: ParameterType::NewFile(ParameterFileType::Raster),
+ default_value: None,
+ optional: true,
+ });
+
+ parameters.push(ToolParameter {
+ name: "Output text?".to_owned(),
+ flags: vec!["--out_text".to_owned()],
+ description: "Would you like to output polygon areas to text?"
+ .to_owned(),
+ parameter_type: ParameterType::Boolean,
+ default_value: None,
+ optional: false,
+ });
+
+ parameters.push(ToolParameter{
+ name: "Units".to_owned(),
+ flags: vec!["--units".to_owned()],
+ description: "Area units; options include 'grid cells' and 'map units'.".to_owned(),
+ parameter_type: ParameterType::OptionList(vec!["grid cells".to_owned(), "map units".to_owned()]),
+ default_value: Some("grid cells".to_owned()),
+ optional: true
+ });
+
+ parameters.push(ToolParameter {
+ name: "Treat zero values as background?".to_owned(),
+ flags: vec!["--zero_back".to_owned()],
+ description: "Flag indicating whether zero values should be treated as a background."
+ .to_owned(),
+ parameter_type: ParameterType::Boolean,
+ default_value: None,
+ optional: false,
+ });
+
+ let sep: String = path::MAIN_SEPARATOR.to_string();
+ let p = format!("{}", env::current_dir().unwrap().display());
+ let e = format!("{}", env::current_exe().unwrap().display());
+ let mut short_exe = e
+ .replace(&p, "")
+ .replace(".exe", "")
+ .replace(".", "")
+ .replace(&sep, "");
+ if e.contains(".exe") {
+ short_exe += ".exe";
+ }
+ let usage = format!(
+ ">>.*{} -r={} -v --wd=\"*path*to*data*\" -i=input.tif -o=output.tif --out_text --units='grid cells' --zero_back",
+ short_exe, name
+ )
+ .replace("*", &sep);
+
+ RasterPerimeter {
+ name: name,
+ description: description,
+ toolbox: toolbox,
+ parameters: parameters,
+ example_usage: usage,
+ }
+ }
+}
+
+impl WhiteboxTool for RasterPerimeter {
+ fn get_source_file(&self) -> String {
+ String::from(file!())
+ }
+
+ fn get_tool_name(&self) -> String {
+ self.name.clone()
+ }
+
+ fn get_tool_description(&self) -> String {
+ self.description.clone()
+ }
+
+ fn get_tool_parameters(&self) -> String {
+ match serde_json::to_string(&self.parameters) {
+ Ok(json_str) => return format!("{{\"parameters\":{}}}", json_str),
+ Err(err) => return format!("{:?}", err),
+ }
+ }
+
+ fn get_example_usage(&self) -> String {
+ self.example_usage.clone()
+ }
+
+ fn get_toolbox(&self) -> String {
+ self.toolbox.clone()
+ }
+
+ fn run<'a>(
+ &self,
+ args: Vec,
+ working_directory: &'a str,
+ verbose: bool,
+ ) -> Result<(), Error> {
+ let mut input_file = String::new();
+ let mut output_file = String::new();
+ let mut output_raster = false;
+ let mut zero_back = false;
+ let mut is_grid_cell_units = false;
+ let mut output_text = false;
+
+ if args.len() == 0 {
+ return Err(Error::new(
+ ErrorKind::InvalidInput,
+ "Tool run with no parameters.",
+ ));
+ }
+ for i in 0..args.len() {
+ let mut arg = args[i].replace("\"", "");
+ arg = arg.replace("\'", "");
+ let cmd = arg.split("="); // in case an equals sign was used
+ let vec = cmd.collect::>();
+ let mut keyval = false;
+ if vec.len() > 1 {
+ keyval = true;
+ }
+ let flag_val = vec[0].to_lowercase().replace("--", "-");
+ if flag_val == "-i" || flag_val == "-input" {
+ input_file = if keyval {
+ vec[1].to_string()
+ } else {
+ args[i + 1].to_string()
+ };
+ } else if flag_val == "-o" || flag_val == "-output" {
+ output_file = if keyval {
+ vec[1].to_string()
+ } else {
+ args[i + 1].to_string()
+ };
+ output_raster = true;
+ } else if flag_val == "-units" {
+ is_grid_cell_units = if keyval {
+ vec[1].to_string().to_lowercase().contains("cells")
+ } else {
+ args[i + 1].to_string().to_lowercase().contains("cells")
+ };
+ } else if flag_val == "-zero_back" {
+ if vec.len() == 1 || !vec[1].to_string().to_lowercase().contains("false") {
+ zero_back = true;
+ }
+ } else if flag_val == "-out_text" {
+ if vec.len() == 1 || !vec[1].to_string().to_lowercase().contains("false") {
+ output_text = true;
+ }
+ }
+ }
+
+ if verbose {
+ println!("***************{}", "*".repeat(self.get_tool_name().len()));
+ println!("* Welcome to {} *", self.get_tool_name());
+ println!("***************{}", "*".repeat(self.get_tool_name().len()));
+ }
+
+ let sep: String = path::MAIN_SEPARATOR.to_string();
+
+ let mut progress: usize;
+ let mut old_progress: usize = 1;
+
+ if !input_file.contains(&sep) && !input_file.contains("/") {
+ input_file = format!("{}{}", working_directory, input_file);
+ }
+
+ if !output_raster && !output_text {
+ println!("Warning: Niether a raster nor text outputs were selected. An area raster will be generated.");
+ output_file = String::from("area.tif");
+ output_raster = true;
+ }
+
+ if output_raster {
+ if !output_file.contains(&sep) && !output_file.contains("/") {
+ output_file = format!("{}{}", working_directory, output_file);
+ }
+ }
+
+ if verbose {
+ println!("Reading data...")
+ };
+
+ let input = Arc::new(Raster::new(&input_file, "r")?);
+
+ let start = Instant::now();
+
+ let lut = [4.000000000f64, 2.828427125, 2.236067977, 2.414213562, 2.828427125, 3.000000000,
+ 2.414213562, 2.236067977, 2.236067977, 2.414213562, 2.000000000,
+ 2.000000000, 2.828427125, 1.414213562, 1.414213562, 1.414213562,
+ 2.236067977, 2.828427125, 2.000000000, 1.414213562, 2.414213562,
+ 1.414213562, 2.000000000, 1.414213562, 2.000000000, 2.000000000,
+ 1.000000000, 2.000000000, 2.000000000, 2.000000000, 2.000000000,
+ 1.000000000, 2.828427125, 3.000000000, 2.828427125, 1.414213562,
+ 2.000000000, 4.000000000, 2.236067977, 2.236067977, 2.414213562,
+ 2.236067977, 1.414213562, 1.414213562, 2.236067977, 2.236067977,
+ 1.414213562, 1.414213562, 2.828427125, 2.236067977, 1.414213562,
+ 1.414213562, 2.236067977, 2.414213562, 2.000000000, 1.414213562, 2.000000000, 2.000000000, 1.000000000,
+ 1.414213562, 2.000000000, 2.000000000, 1.000000000, 1.000000000, 2.236067977, 2.828427125, 2.000000000,
+ 2.000000000, 2.828427125, 2.236067977, 2.000000000, 2.000000000, 2.000000000, 1.414213562, 1.000000000,
+ 2.000000000, 1.414213562, 1.414213562, 1.000000000, 1.414213562, 2.000000000, 1.414213562,
+ 1.000000000, 1.000000000, 1.414213562, 1.414213562, 2.000000000, 1.414213562, 1.000000000, 1.000000000,
+ 0.000000000, 0.000000000, 1.000000000, 1.000000000, 0.000000000, 0.000000000, 2.414213562, 1.414213562,
+ 2.000000000, 2.000000000, 2.236067977, 2.414213562, 2.000000000, 2.000000000, 2.000000000, 1.414213562,
+ 2.000000000, 1.000000000, 2.000000000, 1.414213562, 1.000000000, 1.000000000, 1.414213562, 1.414213562,
+ 1.000000000, 1.000000000, 1.414213562, 1.414213562, 1.000000000, 1.000000000, 2.000000000, 1.414213562,
+ 0.000000000, 0.000000000, 1.000000000, 1.000000000, 0.000000000, 0.000000000, 2.828427125, 2.000000000,
+ 2.828427125, 2.236067977, 3.000000000, 4.000000000, 1.414213562, 2.236067977,
+ 2.828427125, 2.236067977, 1.414213562, 2.000000000, 2.236067977, 2.414213562, 1.414213562, 1.414213562,
+ 2.414213562, 2.236067977, 1.414213562, 1.414213562, 2.236067977, 2.236067977, 1.414213562, 1.414213562,
+ 2.000000000, 2.000000000, 1.000000000, 1.000000000, 2.000000000, 2.000000000, 1.414213562, 1.000000000,
+ 3.000000000, 4.000000000, 2.236067977, 2.414213562, 4.000000000, 4.000000000, 2.414213562, 2.236067977,
+ 1.414213562, 2.236067977, 1.414213562, 1.414213562, 2.414213562, 2.236067977, 1.414213562, 1.414213562,
+ 1.414213562, 2.414213562, 1.414213562, 1.414213562, 2.236067977, 2.236067977,
+ 1.414213562, 1.414213562, 2.000000000, 2.000000000, 1.000000000, 1.000000000, 2.000000000, 2.000000000,
+ 1.000000000, 1.000000000, 2.414213562, 2.000000000, 2.236067977, 2.000000000, 1.414213562, 2.414213562,
+ 2.000000000, 2.000000000, 1.414213562, 1.414213562, 1.000000000, 1.000000000, 1.414213562, 1.414213562,
+ 1.000000000, 1.000000000, 2.000000000, 2.000000000, 2.000000000, 1.000000000, 1.414213562, 1.414213562,
+ 1.000000000, 1.000000000, 2.000000000, 1.000000000, 0.000000000, 0.000000000, 1.414213562, 1.000000000,
+ 0.000000000, 0.000000000, 2.236067977, 2.236067977, 2.000000000, 2.000000000, 2.236067977, 2.236067977,
+ 2.000000000, 2.000000000, 1.414213562, 1.414213562, 1.414213562, 1.000000000, 1.414213562, 1.414213562,
+ 1.000000000, 1.000000000, 1.414213562, 1.414213562, 1.414213562, 1.000000000, 1.414213562, 1.414213562,
+ 1.000000000, 1.000000000, 1.000000000, 1.000000000, 0.000000000, 0.000000000, 1.000000000, 1.000000000,
+ 0.000000000, 0.000000000];
+
+ let dx = [1, 1, 1, 0, -1, -1, -1, 0];
+ let dy = [-1, 0, 1, 1, 1, 0, -1, -1];
+ let v = [1usize, 2, 4, 8, 16, 32, 64, 128];
+ let (mut i, mut j): (isize, isize);
+ let nodata = input.configs.nodata;
+ let rows = input.configs.rows as isize;
+ let columns = input.configs.columns as isize;
+ let resx = input.configs.resolution_x;
+ let resy = input.configs.resolution_y;
+ let avg_res = (resx + resy) / 2f64;
+ let min_val = input.configs.display_min;
+ let max_val = input.configs.display_max;
+ let range = max_val - min_val + 0.00001f64; // otherwise the max value is outside the range
+ let num_bins = range.ceil() as usize;
+ let back_val = if zero_back {
+ 0f64
+ } else {
+ nodata
+ };
+
+ if is_grid_cell_units {
+ let num_procs = num_cpus::get() as isize;
+ let (tx, rx) = mpsc::channel();
+ for tid in 0..num_procs {
+ let input = input.clone();
+ let tx = tx.clone();
+ thread::spawn(move || {
+ let mut data = vec![0f64; num_bins];
+ let mut val: f64;
+ let mut val2: usize;
+ let mut bin: usize;
+ for row in (0..rows).filter(|r| r % num_procs == tid) {
+ for col in 0..columns {
+ val = input.get_value(row, col);
+ if val != nodata && val != back_val && val >= min_val && val <= max_val {
+ bin = (val - min_val).floor() as usize;
+ for n in 0..8 {
+ i = col + dx[n];
+ j = row + dy[n];
+ if input.get_value(j, i) == val {
+ val2 += v[a];
+ }
+ }
+ data[bin] += lut[val2];
+ }
+ }
+ }
+ tx.send(data).unwrap();
+ });
+ }
+
+ let mut data = vec![0usize; num_bins];
+ for tid in 0..num_procs {
+ let data_rx = rx.recv().unwrap();
+ for a in 0..num_bins {
+ data[a] += data_rx[a] * avg_res;
+ }
+
+ if verbose {
+ progress = (100.0_f64 * (tid + 1) as f64 / num_procs as f64) as usize;
+ if progress != old_progress {
+ println!("Progress: {}%", progress);
+ old_progress = progress;
+ }
+ }
+ }
+
+ let mut val: f64;
+ let mut bin: usize;
+ if output_raster {
+ let mut output = Raster::initialize_using_file(&output_file, &input);
+ let out_nodata = -999f64;
+ output.reinitialize_values(out_nodata);
+ output.configs.nodata = out_nodata;
+ output.configs.photometric_interp = PhotometricInterpretation::Continuous;
+ output.configs.data_type = DataType::I32;
+ for row in 0..rows {
+ for col in 0..columns {
+ val = input.get_value(row, col);
+ if val != nodata && val != back_val && val >= min_val && val <= max_val {
+ bin = (val - min_val).floor() as usize;
+ output.set_value(row, col, freq_data[bin] as f64);
+ }
+ }
+ if verbose {
+ progress = (100.0_f64 * row as f64 / (rows - 1) as f64) as usize;
+ if progress != old_progress {
+ println!("Outputting raster: {}%", progress);
+ old_progress = progress;
+ }
+ }
+ }
+
+ let elapsed_time = get_formatted_elapsed_time(start);
+ output.add_metadata_entry(format!(
+ "Created by whitebox_tools\' {} tool",
+ self.get_tool_name()
+ ));
+ output.add_metadata_entry(format!("Input file: {}", input_file));
+ output.add_metadata_entry(format!("Elapsed Time (excluding I/O): {}", elapsed_time));
+
+ if verbose {
+ println!("Saving data...")
+ };
+ let _ = match output.write() {
+ Ok(_) => {
+ if verbose {
+ println!("Output file written")
+ }
+ }
+ Err(e) => return Err(e),
+ };
+ }
+ if output_text {
+ println!("Class,Cells");
+ for a in 0..num_bins {
+ if freq_data[a] > 0 {
+ val = (a as f64 + min_val).floor();
+ println!("{},{}", val, freq_data[a]);
+ }
+ }
+ }
+ } else { // map units
+ let is_geographic = input.is_in_geographic_coordinates();
+ if is_geographic && verbose {
+ println!("Warning: the input file does not appear to be in a projected coordinate system. Area values will only be estimates.");
+ }
+
+ let num_procs = num_cpus::get() as isize;
+ let (tx, rx) = mpsc::channel();
+ for tid in 0..num_procs {
+ let input = input.clone();
+ let tx = tx.clone();
+ thread::spawn(move || {
+ let mut resx = input.configs.resolution_x;
+ let mut resy = input.configs.resolution_y;
+ let mut cell_area = resx * resy;
+ let mut area_data = vec![0f64; num_bins];
+ let mut val: f64;
+ let mut bin: usize;
+ let mut mid_lat: f64;
+ for row in (0..rows).filter(|r| r % num_procs == tid) {
+ if is_geographic {
+ mid_lat = input.get_y_from_row(row).to_radians();
+ resx = resx * 111_111.0 * mid_lat.cos();
+ resy = resy * 111_111.0;
+ cell_area = resx * resy;
+ }
+ for col in 0..columns {
+ val = input.get_value(row, col);
+ if val != nodata && val != back_val && val >= min_val && val <= max_val {
+ bin = (val - min_val).floor() as usize;
+ area_data[bin] += cell_area;
+ }
+ }
+ }
+ tx.send(area_data).unwrap();
+ });
+ }
+
+ // we could just multiply the num cells by the cell area to get the area,
+ // but for the possibility of an input in geographic coordinates where the
+ // cell size is not constant for the data.
+ let mut area_data = vec![0f64; num_bins];
+ for tid in 0..num_procs {
+ let data = rx.recv().unwrap();
+ for a in 0..num_bins {
+ area_data[a] += data[a];
+ }
+
+ if verbose {
+ progress = (100.0_f64 * (tid + 1) as f64 / num_procs as f64) as usize;
+ if progress != old_progress {
+ println!("Progress: {}%", progress);
+ old_progress = progress;
+ }
+ }
+ }
+
+ let mut val: f64;
+ let mut bin: usize;
+ if output_raster {
+ let mut output = Raster::initialize_using_file(&output_file, &input);
+ let out_nodata = -999f64;
+ output.reinitialize_values(out_nodata);
+ output.configs.nodata = out_nodata;
+ output.configs.photometric_interp = PhotometricInterpretation::Continuous;
+ output.configs.data_type = DataType::F32;
+ for row in 0..rows {
+ for col in 0..columns {
+ val = input.get_value(row, col);
+ if val != nodata && val != back_val && val >= min_val && val <= max_val {
+ bin = (val - min_val).floor() as usize;
+ output.set_value(row, col, area_data[bin]);
+ }
+ }
+ if verbose {
+ progress = (100.0_f64 * row as f64 / (rows - 1) as f64) as usize;
+ if progress != old_progress {
+ println!("Outputting raster: {}%", progress);
+ old_progress = progress;
+ }
+ }
+ }
+
+ let elapsed_time = get_formatted_elapsed_time(start);
+ output.add_metadata_entry(format!(
+ "Created by whitebox_tools\' {} tool",
+ self.get_tool_name()
+ ));
+ output.add_metadata_entry(format!("Input file: {}", input_file));
+ output.add_metadata_entry(format!("Elapsed Time (excluding I/O): {}", elapsed_time));
+
+ if verbose {
+ println!("Saving data...")
+ };
+ let _ = match output.write() {
+ Ok(_) => {
+ if verbose {
+ println!("Output file written")
+ }
+ }
+ Err(e) => return Err(e),
+ };
+ }
+ if output_text {
+ println!("Class,Area");
+ for a in 0..num_bins {
+ if area_data[a] > 0f64 {
+ val = (a as f64 + min_val).floor();
+ println!("{},{}", val, area_data[a]);
+ }
+ }
+ }
+ }
+
+ Ok(())
+ }
+}
diff --git a/src/tools/gis_analysis/reclass.rs b/src/tools/gis_analysis/reclass.rs
index fd169e439..e60107b14 100644
--- a/src/tools/gis_analysis/reclass.rs
+++ b/src/tools/gis_analysis/reclass.rs
@@ -156,7 +156,7 @@ impl WhiteboxTool for Reclass {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/gis_analysis/reclass_equal_interval.rs b/src/tools/gis_analysis/reclass_equal_interval.rs
index 4e761e07a..a3f05fc97 100644
--- a/src/tools/gis_analysis/reclass_equal_interval.rs
+++ b/src/tools/gis_analysis/reclass_equal_interval.rs
@@ -153,7 +153,7 @@ impl WhiteboxTool for ReclassEqualInterval {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/gis_analysis/reclass_from_file.rs b/src/tools/gis_analysis/reclass_from_file.rs
index c1e3e329f..37b90d68c 100644
--- a/src/tools/gis_analysis/reclass_from_file.rs
+++ b/src/tools/gis_analysis/reclass_from_file.rs
@@ -147,7 +147,7 @@ impl WhiteboxTool for ReclassFromFile {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/gis_analysis/related_circumscribing_circle.rs b/src/tools/gis_analysis/related_circumscribing_circle.rs
index 7c6f00f42..ba7e3a340 100644
--- a/src/tools/gis_analysis/related_circumscribing_circle.rs
+++ b/src/tools/gis_analysis/related_circumscribing_circle.rs
@@ -136,7 +136,7 @@ impl WhiteboxTool for RelatedCircumscribingCircle {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/gis_analysis/shape_complexity_index.rs b/src/tools/gis_analysis/shape_complexity_index.rs
index 291968146..2c5bd2287 100644
--- a/src/tools/gis_analysis/shape_complexity_index.rs
+++ b/src/tools/gis_analysis/shape_complexity_index.rs
@@ -140,7 +140,7 @@ impl WhiteboxTool for ShapeComplexityIndex {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/gis_analysis/shape_complexity_raster.rs b/src/tools/gis_analysis/shape_complexity_raster.rs
index 9cb8e56a8..d6eaa31e3 100644
--- a/src/tools/gis_analysis/shape_complexity_raster.rs
+++ b/src/tools/gis_analysis/shape_complexity_raster.rs
@@ -128,7 +128,7 @@ impl WhiteboxTool for ShapeComplexityIndexRaster {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/gis_analysis/sibson_interpolation.rs b/src/tools/gis_analysis/sibson_interpolation.rs
index d85641bb6..576d0e67f 100644
--- a/src/tools/gis_analysis/sibson_interpolation.rs
+++ b/src/tools/gis_analysis/sibson_interpolation.rs
@@ -186,7 +186,7 @@ impl WhiteboxTool for SibsonInterpolation {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/gis_analysis/smooth_vectors.rs b/src/tools/gis_analysis/smooth_vectors.rs
index 5480e830d..bd271fedc 100644
--- a/src/tools/gis_analysis/smooth_vectors.rs
+++ b/src/tools/gis_analysis/smooth_vectors.rs
@@ -142,7 +142,7 @@ impl WhiteboxTool for SmoothVectors {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/gis_analysis/snap_endnodes.rs b/src/tools/gis_analysis/snap_endnodes.rs
index 346d40315..43d8e01f8 100644
--- a/src/tools/gis_analysis/snap_endnodes.rs
+++ b/src/tools/gis_analysis/snap_endnodes.rs
@@ -146,7 +146,7 @@ impl WhiteboxTool for SnapEndnodes {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/gis_analysis/split_with_lines.rs b/src/tools/gis_analysis/split_with_lines.rs
index 6c674227b..f7f5dde4d 100644
--- a/src/tools/gis_analysis/split_with_lines.rs
+++ b/src/tools/gis_analysis/split_with_lines.rs
@@ -160,7 +160,7 @@ impl WhiteboxTool for SplitWithLines {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
@@ -688,7 +688,7 @@ impl WhiteboxTool for SplitWithLines {
// now add the endpoints of each polyline into a kd tree
let dimensions = 2;
let capacity_per_node = 64;
- let mut kdtree = KdTree::new_with_capacity(dimensions, capacity_per_node);
+ let mut kdtree = KdTree::with_capacity(dimensions, capacity_per_node);
let mut p: Point2D;
for i in 0..polylines.len() {
p = polylines[i].first_vertex();
diff --git a/src/tools/gis_analysis/sum_overlay.rs b/src/tools/gis_analysis/sum_overlay.rs
index f77ad4fbc..6a9905cea 100644
--- a/src/tools/gis_analysis/sum_overlay.rs
+++ b/src/tools/gis_analysis/sum_overlay.rs
@@ -119,7 +119,7 @@ impl WhiteboxTool for SumOverlay {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/gis_analysis/symmetrical_difference.rs b/src/tools/gis_analysis/symmetrical_difference.rs
index 6ca77f60b..a7067e921 100644
--- a/src/tools/gis_analysis/symmetrical_difference.rs
+++ b/src/tools/gis_analysis/symmetrical_difference.rs
@@ -185,7 +185,7 @@ impl WhiteboxTool for SymmetricalDifference {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
@@ -310,7 +310,7 @@ impl WhiteboxTool for SymmetricalDifference {
// place the points from both files into a KD-tree
let dimensions = 2;
let capacity_per_node = 64;
- let mut tree = KdTree::new_with_capacity(dimensions, capacity_per_node);
+ let mut tree = KdTree::with_capacity(dimensions, capacity_per_node);
let mut p: Point2D;
for record_num in 0..input.num_records {
let record = input.get_record(record_num);
@@ -406,7 +406,7 @@ impl WhiteboxTool for SymmetricalDifference {
// place the points from both files into a KD-tree
let dimensions = 2;
let capacity_per_node = 64;
- let mut tree = KdTree::new_with_capacity(dimensions, capacity_per_node);
+ let mut tree = KdTree::with_capacity(dimensions, capacity_per_node);
let mut p: Point2D;
let mut total_points = 0;
for record_num in 0..input.num_records {
@@ -632,7 +632,7 @@ impl WhiteboxTool for SymmetricalDifference {
// Break the polylines up into shorter lines at junction points.
let dimensions = 2;
let capacity_per_node = 64;
- let mut tree = KdTree::new_with_capacity(dimensions, capacity_per_node);
+ let mut tree = KdTree::with_capacity(dimensions, capacity_per_node);
let mut p: Point2D;
for i in 0..polylines.len() {
for j in 0..polylines[i].len() {
@@ -870,7 +870,7 @@ impl WhiteboxTool for SymmetricalDifference {
let mut p: Point2D;
let dimensions = 2;
let capacity_per_node = 64;
- let mut tree = KdTree::new_with_capacity(dimensions, capacity_per_node);
+ let mut tree = KdTree::with_capacity(dimensions, capacity_per_node);
for i in 0..polygons.len() {
for j in 0..polygons[i].len() {
p = polygons[i][j];
@@ -1086,7 +1086,7 @@ impl WhiteboxTool for SymmetricalDifference {
// now add the endpoints of each polyline into a kd tree
let dimensions = 2;
let capacity_per_node = 64;
- let mut kdtree = KdTree::new_with_capacity(dimensions, capacity_per_node);
+ let mut kdtree = KdTree::with_capacity(dimensions, capacity_per_node);
let mut p1: Point2D;
let mut p2: Point2D;
let mut p3: Point2D;
diff --git a/src/tools/gis_analysis/tin_gridding.rs b/src/tools/gis_analysis/tin_gridding.rs
index a16ff115f..974712be7 100644
--- a/src/tools/gis_analysis/tin_gridding.rs
+++ b/src/tools/gis_analysis/tin_gridding.rs
@@ -93,6 +93,15 @@ impl TINGridding {
optional: false,
});
+ parameters.push(ToolParameter {
+ name: "Maximum Triangle Edge Length (optional)".to_owned(),
+ flags: vec!["--max_triangle_edge_length".to_owned()],
+ description: "Optional maximum triangle edge length; triangles larger than this size will not be gridded.".to_owned(),
+ parameter_type: ParameterType::Float,
+ default_value: None,
+ optional: true,
+ });
+
let sep: String = path::MAIN_SEPARATOR.to_string();
let p = format!("{}", env::current_dir().unwrap().display());
let e = format!("{}", env::current_exe().unwrap().display());
@@ -167,12 +176,13 @@ impl WhiteboxTool for TINGridding {
let mut use_field = false;
let mut output_file: String = "".to_string();
let mut grid_res: f64 = 1.0;
+ let mut max_triangle_edge_length = f64::INFINITY;
// read the arguments
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
@@ -214,6 +224,14 @@ impl WhiteboxTool for TINGridding {
} else {
args[i + 1].to_string().parse::().unwrap()
};
+ } else if flag_val == "-max_triangle_edge_length" {
+ max_triangle_edge_length = if keyval {
+ vec[1].to_string().parse::().unwrap()
+ } else {
+ args[i + 1].to_string().parse::().unwrap()
+ };
+
+ max_triangle_edge_length *= max_triangle_edge_length; // actually squared distance
}
}
@@ -370,42 +388,46 @@ impl WhiteboxTool for TINGridding {
p1 = delaunay.triangles[i];
p2 = delaunay.triangles[i + 1];
p3 = delaunay.triangles[i + 2];
- tri_points[0] = points[p1].clone();
- tri_points[1] = points[p2].clone();
- tri_points[2] = points[p3].clone();
- tri_points[3] = points[p1].clone();
- // if is_clockwise_order(&tri_points) {
- // tri_points.reverse();
- // }
-
- // get the equation of the plane
- a = Vector3::new(tri_points[0].x, tri_points[0].y, z_values[p1]);
- b = Vector3::new(tri_points[1].x, tri_points[1].y, z_values[p2]);
- c = Vector3::new(tri_points[2].x, tri_points[2].y, z_values[p3]);
- norm = (b - a).cross(&(c - a));
-
- if norm.z != 0f64 {
- k = -(tri_points[0].x * norm.x + tri_points[0].y * norm.y + norm.z * z_values[p1]);
-
- // find grid intersections with this triangle
- bottom = points[p1].y.min(points[p2].y.min(points[p3].y));
- top = points[p1].y.max(points[p2].y.max(points[p3].y));
- left = points[p1].x.min(points[p2].x.min(points[p3].x));
- right = points[p1].x.max(points[p2].x.max(points[p3].x));
-
- bottom_row = ((north - bottom) / grid_res).ceil() as isize;
- top_row = ((north - top) / grid_res).floor() as isize;
- left_col = ((left - west) / grid_res).floor() as isize;
- right_col = ((right - west) / grid_res).ceil() as isize;
-
- for row in top_row..=bottom_row {
- for col in left_col..=right_col {
- x = west + (col as f64 + 0.5) * grid_res;
- y = north - (row as f64 + 0.5) * grid_res;
- if point_in_poly(&Point2D::new(x, y), &tri_points) {
- // calculate the z values
- z = -(norm.x * x + norm.y * y + k) / norm.z;
- output.set_value(row, col, z);
+ if max_distance_squared(points[p1], points[p2], points[p3], z_values[p1],
+ z_values[p2], z_values[p3]) < max_triangle_edge_length {
+
+ tri_points[0] = points[p1].clone();
+ tri_points[1] = points[p2].clone();
+ tri_points[2] = points[p3].clone();
+ tri_points[3] = points[p1].clone();
+ // if is_clockwise_order(&tri_points) {
+ // tri_points.reverse();
+ // }
+
+ // get the equation of the plane
+ a = Vector3::new(tri_points[0].x, tri_points[0].y, z_values[p1]);
+ b = Vector3::new(tri_points[1].x, tri_points[1].y, z_values[p2]);
+ c = Vector3::new(tri_points[2].x, tri_points[2].y, z_values[p3]);
+ norm = (b - a).cross(&(c - a));
+
+ if norm.z != 0f64 {
+ k = -(tri_points[0].x * norm.x + tri_points[0].y * norm.y + norm.z * z_values[p1]);
+
+ // find grid intersections with this triangle
+ bottom = points[p1].y.min(points[p2].y.min(points[p3].y));
+ top = points[p1].y.max(points[p2].y.max(points[p3].y));
+ left = points[p1].x.min(points[p2].x.min(points[p3].x));
+ right = points[p1].x.max(points[p2].x.max(points[p3].x));
+
+ bottom_row = ((north - bottom) / grid_res).ceil() as isize;
+ top_row = ((north - top) / grid_res).floor() as isize;
+ left_col = ((left - west) / grid_res).floor() as isize;
+ right_col = ((right - west) / grid_res).ceil() as isize;
+
+ for row in top_row..=bottom_row {
+ for col in left_col..=right_col {
+ x = west + (col as f64 + 0.5) * grid_res;
+ y = north - (row as f64 + 0.5) * grid_res;
+ if point_in_poly(&Point2D::new(x, y), &tri_points) {
+ // calculate the z values
+ z = -(norm.x * x + norm.y * y + k) / norm.z;
+ output.set_value(row, col, z);
+ }
}
}
}
@@ -451,3 +473,31 @@ impl WhiteboxTool for TINGridding {
Ok(())
}
}
+
+/// Calculate squared Euclidean distance between the point and another.
+pub fn max_distance_squared(p1: Point2D, p2: Point2D, p3: Point2D, z1: f64, z2: f64, z3: f64) -> f64 {
+ let mut dx = p1.x - p2.x;
+ let mut dy = p1.y - p2.y;
+ let mut dz = z1 - z2;
+ let mut max_dist = dx * dx + dy * dy + dz * dz;
+
+ dx = p1.x - p3.x;
+ dy = p1.y - p3.y;
+ dz = z1 - z3;
+ let mut dist = dx * dx + dy * dy + dz * dz;
+
+ if dist > max_dist {
+ max_dist = dist
+ }
+
+ dx = p2.x - p3.x;
+ dy = p2.y - p3.y;
+ dz = z2 - z3;
+ dist = dx * dx + dy * dy + dz * dz;
+
+ if dist > max_dist {
+ max_dist = dist
+ }
+
+ max_dist
+}
\ No newline at end of file
diff --git a/src/tools/gis_analysis/union.rs b/src/tools/gis_analysis/union.rs
index c41c3ae9f..47a46dfc2 100644
--- a/src/tools/gis_analysis/union.rs
+++ b/src/tools/gis_analysis/union.rs
@@ -178,7 +178,7 @@ impl WhiteboxTool for Union {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
@@ -303,7 +303,7 @@ impl WhiteboxTool for Union {
// place the points from both files into a KD-tree
let dimensions = 2;
let capacity_per_node = 64;
- let mut tree = KdTree::new_with_capacity(dimensions, capacity_per_node);
+ let mut tree = KdTree::with_capacity(dimensions, capacity_per_node);
let mut p: Point2D;
for record_num in 0..input.num_records {
let record = input.get_record(record_num);
@@ -420,7 +420,7 @@ impl WhiteboxTool for Union {
// place the points from both files into a KD-tree
let dimensions = 2;
let capacity_per_node = 64;
- let mut tree = KdTree::new_with_capacity(dimensions, capacity_per_node);
+ let mut tree = KdTree::with_capacity(dimensions, capacity_per_node);
let mut p: Point2D;
let mut total_points = 0;
for record_num in 0..input.num_records {
@@ -600,7 +600,7 @@ impl WhiteboxTool for Union {
// Break the polylines up into shorter lines at junction points.
let dimensions = 2;
let capacity_per_node = 64;
- let mut tree = KdTree::new_with_capacity(dimensions, capacity_per_node);
+ let mut tree = KdTree::with_capacity(dimensions, capacity_per_node);
let mut p: Point2D;
for i in 0..polylines.len() {
for j in 0..polylines[i].len() {
@@ -732,7 +732,7 @@ impl WhiteboxTool for Union {
// }
// }
- let mut tree = KdTree::new_with_capacity(dimensions, capacity_per_node);
+ let mut tree = KdTree::with_capacity(dimensions, capacity_per_node);
let mut p1: Point2D;
let mut p2: Point2D;
for i in 0..features_polylines.len() {
@@ -950,9 +950,9 @@ impl WhiteboxTool for Union {
// Break the polygons up into line segments at junction points and endnodes.
let mut p: Point2D;
- let dimensions = 2;
- let capacity_per_node = 64;
- let mut tree = KdTree::new_with_capacity(dimensions, capacity_per_node);
+ const DIMENSIONS: usize = 2;
+ const CAPACITY_PER_NODE: usize = 64;
+ let mut tree = KdTree::with_capacity(DIMENSIONS, CAPACITY_PER_NODE);
for i in 0..polygons.len() {
for j in 0..polygons[i].len() {
p = polygons[i][j];
@@ -1162,7 +1162,7 @@ impl WhiteboxTool for Union {
// now add the endpoints of each polyline into a kd tree
let dimensions = 2;
let capacity_per_node = 64;
- let mut kdtree = KdTree::new_with_capacity(dimensions, capacity_per_node);
+ let mut kdtree = KdTree::with_capacity(dimensions, capacity_per_node);
let mut p1: Point2D;
let mut p2: Point2D;
let mut p3: Point2D;
diff --git a/src/tools/gis_analysis/vector_hex_bin.rs b/src/tools/gis_analysis/vector_hex_bin.rs
index 2ec74231c..9acbcd493 100644
--- a/src/tools/gis_analysis/vector_hex_bin.rs
+++ b/src/tools/gis_analysis/vector_hex_bin.rs
@@ -171,7 +171,7 @@ impl WhiteboxTool for VectorHexBinning {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/gis_analysis/voronoi_diagram.rs b/src/tools/gis_analysis/voronoi_diagram.rs
index a03fa235d..b5212cbdc 100644
--- a/src/tools/gis_analysis/voronoi_diagram.rs
+++ b/src/tools/gis_analysis/voronoi_diagram.rs
@@ -7,8 +7,7 @@ License: MIT
*/
use crate::algorithms::triangulate;
-use crate::structures::BoundingBox;
-use crate::structures::Point2D;
+use crate::structures::{BoundingBox, Point2D};
use crate::tools::*;
use crate::vector::*;
use std::collections::HashMap;
@@ -154,7 +153,7 @@ impl WhiteboxTool for VoronoiDiagram {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
@@ -220,9 +219,7 @@ impl WhiteboxTool for VoronoiDiagram {
// set the projection information
output.projection = input.projection.clone();
- output
- .attributes
- .add_fields(&input.attributes.get_fields().clone());
+ output.attributes.add_fields(&input.attributes.get_fields().clone());
// Read the points in
let mut points: Vec = vec![];
diff --git a/src/tools/gis_analysis/weighted_overlay.rs b/src/tools/gis_analysis/weighted_overlay.rs
index 120cefbde..2c244ae90 100644
--- a/src/tools/gis_analysis/weighted_overlay.rs
+++ b/src/tools/gis_analysis/weighted_overlay.rs
@@ -174,7 +174,7 @@ impl WhiteboxTool for WeightedOverlay {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/gis_analysis/weighted_sum.rs b/src/tools/gis_analysis/weighted_sum.rs
index 3acbdf4af..e85a6df75 100644
--- a/src/tools/gis_analysis/weighted_sum.rs
+++ b/src/tools/gis_analysis/weighted_sum.rs
@@ -133,7 +133,7 @@ impl WhiteboxTool for WeightedSum {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/hydro_analysis/average_flowpath_slope.rs b/src/tools/hydro_analysis/average_flowpath_slope.rs
index c2e4686e7..0fdecabb1 100644
--- a/src/tools/hydro_analysis/average_flowpath_slope.rs
+++ b/src/tools/hydro_analysis/average_flowpath_slope.rs
@@ -130,7 +130,7 @@ impl WhiteboxTool for AverageFlowpathSlope {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/hydro_analysis/average_upslope_flowpath_length.rs b/src/tools/hydro_analysis/average_upslope_flowpath_length.rs
index 79c4719f0..b7bff9bd7 100644
--- a/src/tools/hydro_analysis/average_upslope_flowpath_length.rs
+++ b/src/tools/hydro_analysis/average_upslope_flowpath_length.rs
@@ -130,7 +130,7 @@ impl WhiteboxTool for AverageUpslopeFlowpathLength {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/hydro_analysis/basins.rs b/src/tools/hydro_analysis/basins.rs
index 79cb07851..e7a27c298 100644
--- a/src/tools/hydro_analysis/basins.rs
+++ b/src/tools/hydro_analysis/basins.rs
@@ -149,7 +149,7 @@ impl WhiteboxTool for Basins {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/hydro_analysis/breach_depressions.rs b/src/tools/hydro_analysis/breach_depressions.rs
index f2b5e9bab..9aa85b521 100644
--- a/src/tools/hydro_analysis/breach_depressions.rs
+++ b/src/tools/hydro_analysis/breach_depressions.rs
@@ -2,7 +2,7 @@
This tool is part of the WhiteboxTools geospatial analysis library.
Authors: Dr. John Lindsay
Created: 28/06/2017
-Last Modified: 18/10/2019
+Last Modified: 24/11/2019
License: MIT
*/
@@ -41,6 +41,13 @@ use std::path;
/// the need to precisely represent small elevation differences along flats. Therefore, if the input DEM is stored
/// at a lower level of precision (e.g. 32-bit floating point elevations), this may result in a doubling of
/// the size of the DEM.
+///
+/// In comparison with the `BreachDepressionsLeastCost` tool, this breaching method often provides a less
+/// satisfactory, higher impact, breaching solution and is often less efficient. **It has been provided to users for
+/// legacy reasons and it is advisable that users try the `BreachDepressionsLeastCost` tool to remove depressions from
+/// their DEMs first**. The `BreachDepressionsLeastCost` tool is particularly
+/// well suited to breaching through road embankments. Nonetheless, there are applications for which full depression filling
+/// using the `FillDepressions` tool may be preferred.
///
/// # Reference
/// Lindsay JB. 2016. *Efficient hybrid breaching-filling sink removal methods for
@@ -48,7 +55,7 @@ use std::path;
/// 30(6): 846–857. DOI: 10.1002/hyp.10648
///
/// # See Also
-/// `FillDepressions`, `FillSingleCellPits`
+/// `BreachDepressionsLeastCost`, `FillDepressions`, `FillSingleCellPits`
pub struct BreachDepressions {
name: String,
description: String,
@@ -194,7 +201,7 @@ impl WhiteboxTool for BreachDepressions {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
@@ -279,14 +286,16 @@ impl WhiteboxTool for BreachDepressions {
let columns = input.configs.columns as isize;
let num_cells = rows * columns;
let nodata = input.configs.nodata;
+ let resx = input.configs.resolution_x;
+ let resy = input.configs.resolution_y;
+ let diagres = (resx * resx + resy * resy).sqrt();
- let small_num = if !flat_increment.is_nan() {
+ let small_num = if !flat_increment.is_nan() || flat_increment == 0f64 {
flat_increment
} else {
- let min_val = input.configs.minimum;
- let elev_digits = ((input.configs.maximum - min_val) as i64).to_string().len();
+ let elev_digits = (input.configs.maximum as i64).to_string().len();
let elev_multiplier = 10.0_f64.powi((6 - elev_digits) as i32);
- 1.0_f64 / elev_multiplier as f64
+ 1.0_f64 / elev_multiplier as f64 * diagres.ceil()
};
let mut z: f64;
@@ -315,7 +324,7 @@ impl WhiteboxTool for BreachDepressions {
}
}
if flag {
- input.set_value(row, col, min_zn + small_num);
+ input.set_value(row, col, min_zn - small_num);
}
}
}
diff --git a/src/tools/hydro_analysis/breach_depressions_least_cost.rs b/src/tools/hydro_analysis/breach_depressions_least_cost.rs
new file mode 100644
index 000000000..056f41371
--- /dev/null
+++ b/src/tools/hydro_analysis/breach_depressions_least_cost.rs
@@ -0,0 +1,1181 @@
+/*
+This tool is part of the WhiteboxTools geospatial analysis library.
+Authors: Dr. John Lindsay
+Created: 01/11/2019
+Last Modified: 24/11/2019
+License: MIT
+*/
+
+use crate::raster::*;
+use crate::structures::Array2D;
+use crate::tools::*;
+use std::cmp::Ordering::Equal;
+use std::cmp::Ordering;
+use std::collections::{BinaryHeap, VecDeque};
+use std::env;
+use std::f64;
+use std::i32;
+use std::io::{Error, ErrorKind};
+use std::path;
+use std::sync::mpsc;
+use std::sync::Arc;
+use std::thread;
+
+/// This tool can be used to perform a type of optimal depression breaching to prepare a
+/// digital elevation model (DEM) for hydrological analysis. Depression breaching is a common
+/// alternative to depression filling (`FillDepression`) and often offers a lower-impact
+/// solution to the removal of topographic depressions. This tool implements a method that is
+/// loosely based on the algorithm described by Lindsay and Dhun (2015), furthering the earlier
+/// algorithm with efficiency optimizations and other significant enhancements. The approach uses a least-cost
+/// path analysis to identify the breach channel that connects pit cells (i.e. grid cells for
+/// which there is no lower neighbour) to some distant lower cell. Here, the cost of a breach
+/// path is determined by the amount of elevation lowering needed to cut the breach channel
+/// through the surrounding topography.
+///
+/// The user must specify the name of the input DEM file (`--dem`), the output breached DEM
+/// file (`--output`), the maximum search window radius (`--radius`), the optional maximum breach
+/// cost (`--max_cost`), and an optional flat height increment value (`--flat_increment`). Notice that if the
+/// `--flat_increment` parameter is not specified, the small number used to ensure flow across flats will be
+/// calculated automatically, which should be preferred in most applications of the tool.
+/// The tool operates by performing a least-cost path analysis within a neighbourhood surround
+/// each pit cell, where the neighbourhood size begins small (9 × 9; radius = 4) and iteratively increases (by doubling)
+/// until it reaches the maximum breach length parameter. If a value is specified for the optional
+/// `--max_cost` parameter, then least-cost breach paths that would require digging a channel that is more costly
+/// than this value will be left to see if the pit cell can be resolved with a longer but shallower (less costly) breach
+/// channel in an additional iteration (i.e. a larger search window). The flat increment value is used
+/// to ensure that there is a monotonically descending path along breach channels to satisfy the necessary
+/// condition of a downslope gradient for flowpath modelling. It is best for this value to be a small
+/// value. If left unspecified, the tool with determine an appropriate value based on the range of
+/// elevation values in the input DEM. Notice that the need to specify these very small elevation
+/// increment values is one of the reasons why the output DEM will always be of a 64-bit floating-point
+/// data type, which will often double the storage requirements of a DEM (DEMs are often store with 32-bit
+/// precision). Lastly, the user may optionally choose to apply depression filling (`--fill`) on any depressions
+/// that remain unresolved by the earlier depression breaching operation. This filling step uses an efficient
+/// filling method based on flooding depressions from their pit cells until outlets are identified and then
+/// raising the elevations of flooded cells back and away from the outlets.
+///
+/// The tool can be run in two modes, based on whether the `--min_dist` is specified. If the `--min_dist` flag
+/// is specified, the accumulated cost (accum2) of breaching from *cell1* to *cell2* along a channel
+/// issuing from *pit* is calculated using the traditional cost-distance function:
+///
+/// > cost1 = z1 - (zpit + *l* × *s*)
+/// >
+/// > cost2 = z2 - [zpit + (*l* + 1)*s*]
+/// >
+/// > accum2 = accum1 + *g*(cost1 + cost2) / 2.0
+///
+/// where cost1 and cost2 are the costs associated with moving through *cell1* and *cell2*
+/// respectively, z1 and z2 are the elevations of the two cells, zpit is the elevation
+/// of the pit cell, *l* is the length of the breach channel to *cell1*, *g* is the grid cell distance between
+/// cells (accounting for diagonal distances), and *s* is the small number used to ensure flow
+/// across flats. If the `--min_dist` flag is not present, the accumulated cost is calculated as:
+///
+/// > accum2 = accum1 + cost2
+///
+/// That is, without the `--min_dist` flag, the tool works to minimize elevation changes to the DEM caused by
+/// breaching, without considering the distance of breach channels. Notice that the value `--max_cost`, if
+/// specified, should account for this difference in the way cost/cost-distances are calculated. The first cell
+/// in the least-cost accumulation operation that is identified for which cost2 <= 0.0 is the target
+/// cell to which the breach channel will connect the pit along the least-cost path.
+///
+/// In comparison with the `BreachDepressions` tool, this breaching method often provides a more
+/// satisfactory, lower impact, breaching solution and is often more efficient. It is therefore advisable that users
+/// try the `BreachDepressionsLeastCost` tool to remove depressions from their DEMs first. This tool is particularly
+/// well suited to breaching through road embankments. There are instances when a breaching solution is inappropriate, e.g.
+/// when a very deep depression such as an open-pit mine occurs in the DEM and long, deep breach paths are created. Often
+/// restricting breaching with the `--max_cost` parameter, combined with subsequent depression filling (`--fill`) can
+/// provide an adequate solution in these cases. Nonetheless, there are applications for which full depression filling
+/// using the `FillDepressions` tool may be preferred.
+///
+/// # Reference
+/// Lindsay J, Dhun K. 2015. Modelling surface drainage patterns in altered landscapes using LiDAR.
+/// *International Journal of Geographical Information Science*, 29: 1-15. DOI: 10.1080/13658816.2014.975715
+///
+/// # See Also
+/// `BreachDepressions`, `FillDepressions`, `CostPathway`
+pub struct BreachDepressionsLeastCost {
+ name: String,
+ description: String,
+ toolbox: String,
+ parameters: Vec,
+ example_usage: String,
+}
+
+impl BreachDepressionsLeastCost {
+ pub fn new() -> BreachDepressionsLeastCost {
+ // public constructor
+ let name = "BreachDepressionsLeastCost".to_string();
+ let toolbox = "Hydrological Analysis".to_string();
+ let description = "Breaches the depressions in a DEM using a least-cost pathway method.".to_string();
+
+ let mut parameters = vec![];
+ parameters.push(ToolParameter {
+ name: "Input DEM File".to_owned(),
+ flags: vec!["-i".to_owned(), "--dem".to_owned()],
+ description: "Input raster DEM file.".to_owned(),
+ parameter_type: ParameterType::ExistingFile(ParameterFileType::Raster),
+ default_value: None,
+ optional: false,
+ });
+
+ parameters.push(ToolParameter {
+ name: "Output File".to_owned(),
+ flags: vec!["-o".to_owned(), "--output".to_owned()],
+ description: "Output raster file.".to_owned(),
+ parameter_type: ParameterType::NewFile(ParameterFileType::Raster),
+ default_value: None,
+ optional: false,
+ });
+
+ parameters.push(ToolParameter {
+ name: "Maximum Search Window Radius (cells)".to_owned(),
+ flags: vec!["--radius".to_owned()],
+ description: ""
+ .to_owned(),
+ parameter_type: ParameterType::Integer,
+ default_value: None,
+ optional: false,
+ });
+
+ parameters.push(ToolParameter {
+ name: "Maximum Breach Cost (z units)".to_owned(),
+ flags: vec!["--max_cost".to_owned()],
+ description: "Optional maximum breach cost (default is Inf).".to_owned(),
+ parameter_type: ParameterType::Float,
+ default_value: None,
+ optional: true,
+ });
+
+ parameters.push(ToolParameter {
+ name: "Minimize breach distances?".to_owned(),
+ flags: vec!["--min_dist".to_owned()],
+ description: "Optional flag indicating whether to minimize breach distances.".to_owned(),
+ parameter_type: ParameterType::Boolean,
+ default_value: Some("true".to_string()),
+ optional: true,
+ });
+
+ parameters.push(ToolParameter {
+ name: "Flat increment value (z units)".to_owned(),
+ flags: vec!["--flat_increment".to_owned()],
+ description: "Optional elevation increment applied to flat areas.".to_owned(),
+ parameter_type: ParameterType::Float,
+ default_value: None,
+ optional: true,
+ });
+
+ parameters.push(ToolParameter {
+ name: "Fill unbreached depressions?".to_owned(),
+ flags: vec!["--fill".to_owned()],
+ description: "Optional flag indicating whether to fill any remaining unbreached depressions.".to_owned(),
+ parameter_type: ParameterType::Boolean,
+ default_value: Some("true".to_string()),
+ optional: true,
+ });
+
+ let sep: String = path::MAIN_SEPARATOR.to_string();
+ let p = format!("{}", env::current_dir().unwrap().display());
+ let e = format!("{}", env::current_exe().unwrap().display());
+ let mut short_exe = e
+ .replace(&p, "")
+ .replace(".exe", "")
+ .replace(".", "")
+ .replace(&sep, "");
+ if e.contains(".exe") {
+ short_exe += ".exe";
+ }
+ let usage = format!(
+ ">>.*{0} -r={1} -v --wd=\"*path*to*data*\" --dem=DEM.tif -o=output.tif",
+ short_exe, name
+ )
+ .replace("*", &sep);
+
+ BreachDepressionsLeastCost {
+ name: name,
+ description: description,
+ toolbox: toolbox,
+ parameters: parameters,
+ example_usage: usage,
+ }
+ }
+}
+
+impl WhiteboxTool for BreachDepressionsLeastCost {
+ fn get_source_file(&self) -> String {
+ String::from(file!())
+ }
+
+ fn get_tool_name(&self) -> String {
+ self.name.clone()
+ }
+
+ fn get_tool_description(&self) -> String {
+ self.description.clone()
+ }
+
+ fn get_tool_parameters(&self) -> String {
+ match serde_json::to_string(&self.parameters) {
+ Ok(json_str) => return format!("{{\"parameters\":{}}}", json_str),
+ Err(err) => return format!("{:?}", err),
+ }
+ }
+
+ fn get_example_usage(&self) -> String {
+ self.example_usage.clone()
+ }
+
+ fn get_toolbox(&self) -> String {
+ self.toolbox.clone()
+ }
+
+ fn run<'a>(
+ &self,
+ args: Vec,
+ working_directory: &'a str,
+ verbose: bool,
+ ) -> Result<(), Error> {
+ let mut input_file = String::new();
+ let mut output_file = String::new();
+ let mut max_cost = f64::INFINITY;
+ let mut end_radius = 20isize;
+ let mut flat_increment = f64::NAN;
+ let mut fill_deps = false;
+ let mut minimize_dist = false;
+
+ if args.len() == 0 {
+ return Err(Error::new(
+ ErrorKind::InvalidInput,
+ "Tool run with no parameters.",
+ ));
+ }
+ for i in 0..args.len() {
+ let mut arg = args[i].replace("\"", "");
+ arg = arg.replace("\'", "");
+ let cmd = arg.split("="); // in case an equals sign was used
+ let vec = cmd.collect::>();
+ let mut keyval = false;
+ if vec.len() > 1 {
+ keyval = true;
+ }
+ let flag_val = vec[0].to_lowercase().replace("--", "-");
+ if flag_val == "-i" || flag_val == "-input" || flag_val == "-dem" {
+ input_file = if keyval {
+ vec[1].to_string()
+ } else {
+ args[i + 1].to_string()
+ };
+ } else if flag_val == "-o" || flag_val == "-output" {
+ output_file = if keyval {
+ vec[1].to_string()
+ } else {
+ args[i + 1].to_string()
+ };
+ } else if flag_val == "-radius" {
+ end_radius = if keyval {
+ vec[1].to_string().parse::().unwrap()
+ } else {
+ args[i + 1].to_string().parse::().unwrap()
+ };
+ } else if flag_val == "-max_cost" {
+ max_cost = if keyval {
+ vec[1].to_string().parse::().unwrap()
+ } else {
+ args[i + 1].to_string().parse::().unwrap()
+ };
+ } else if flag_val == "-flat_increment" {
+ flat_increment = if keyval {
+ vec[1].to_string().parse::().unwrap()
+ } else {
+ args[i + 1].to_string().parse::().unwrap()
+ };
+ } else if flag_val == "-min_dist" {
+ if vec.len() == 1 || !vec[1].to_string().to_lowercase().contains("false") {
+ minimize_dist = true;
+ }
+ } else if flag_val == "-fill" {
+ if vec.len() == 1 || !vec[1].to_string().to_lowercase().contains("false") {
+ fill_deps = true;
+ }
+ }
+ }
+
+ if verbose {
+ println!("***************{}", "*".repeat(self.get_tool_name().len()));
+ println!("* Welcome to {} *", self.get_tool_name());
+ println!("***************{}", "*".repeat(self.get_tool_name().len()));
+ }
+
+ let sep: String = path::MAIN_SEPARATOR.to_string();
+
+ let mut progress: usize;
+ let mut old_progress: usize = 1;
+
+ if !input_file.contains(&sep) && !input_file.contains("/") {
+ input_file = format!("{}{}", working_directory, input_file);
+ }
+ if !output_file.contains(&sep) && !output_file.contains("/") {
+ output_file = format!("{}{}", working_directory, output_file);
+ }
+
+ if verbose {
+ println!("Reading data...")
+ };
+
+ let input = Arc::new(Raster::new(&input_file, "r")?);
+
+ let start = Instant::now();
+
+ let rows = input.configs.rows as isize;
+ let columns = input.configs.columns as isize;
+ let nodata = input.configs.nodata;
+ let (mut col, mut row): (isize, isize);
+ let (mut rn, mut cn): (isize, isize);
+ let mut accum: f64;
+ let (mut z, mut zn, mut zout): (f64, f64, f64);
+ let dx = [1, 1, 1, 0, -1, -1, -1, 0];
+ let dy = [-1, 0, 1, 1, 1, 0, -1, -1];
+ let mut flag: bool;
+ let mut num_solved: usize;
+ let mut overall_num_solved = 0;
+ let mut num_unsolved = 0;
+ let resx = input.configs.resolution_x;
+ let resy = input.configs.resolution_y;
+ let diagres = (resx * resx + resy * resy).sqrt();
+ let cost_dist = [diagres, resx, diagres, resy, diagres, resx, diagres, resy];
+ let mut cost1: f64;
+ let mut cost2: f64;
+ let mut new_cost: f64;
+ let mut length: i16;
+ let mut b: usize;
+ let num_procs = num_cpus::get() as isize;
+
+ let small_num = if !flat_increment.is_nan() || flat_increment == 0f64 {
+ flat_increment
+ } else {
+ let elev_digits = (input.configs.maximum as i32).to_string().len();
+ let elev_multiplier = 10.0_f64.powi((15 - elev_digits) as i32);
+ 1.0_f64 / elev_multiplier as f64 * diagres.ceil()
+ };
+
+ let mut output = Raster::initialize_using_file(&output_file, &input);
+ // Even if the input is f32, the output will need to be 64-bit to represent the small elevation differences
+ output.configs.data_type = DataType::F64;
+ let display_min = input.configs.display_min;
+ let display_max = input.configs.display_max;
+
+ // Raise pit cells to minimize the depth of breach channels.
+ let (tx, rx) = mpsc::channel();
+ for tid in 0..num_procs {
+ let input = input.clone();
+ let tx = tx.clone();
+ thread::spawn(move || {
+ let (mut z, mut zn, mut min_zn): (f64, f64, f64);
+ let mut flag: bool;
+ let dx = [1, 1, 1, 0, -1, -1, -1, 0];
+ let dy = [-1, 0, 1, 1, 1, 0, -1, -1];
+ for row in (0..rows).filter(|r| r % num_procs == tid) {
+ let mut data = input.get_row_data(row);
+ for col in 0..columns {
+ z = input.get_value(row, col);
+ if z != nodata {
+ flag = true;
+ min_zn = f64::INFINITY;
+ for n in 0..8 {
+ zn = input.get_value(row + dy[n], col + dx[n]);
+ if zn < min_zn {
+ min_zn = zn;
+ }
+ if zn == nodata { // It's an edge cell.
+ flag = false;
+ break;
+ }
+ if zn < z { // There's a lower neighbour
+ flag = false;
+ break;
+ }
+ }
+ if flag {
+ data[col as usize] = min_zn - small_num;
+ }
+ }
+ }
+ tx.send((row, data)).unwrap();
+ }
+ });
+ }
+
+ for r in 0..rows {
+ let (row, data) = rx.recv().unwrap();
+ output.set_row_data(row, data);
+
+ if verbose {
+ progress = (100.0_f64 * r as f64 / (rows - 1) as f64) as usize;
+ if progress != old_progress {
+ println!("Filling pits: {}%", progress);
+ old_progress = progress;
+ }
+ }
+ }
+
+ // drop(input); // input is no longer needed.
+
+
+ //////////////////////////////////////////////////////////////////////////////////////
+ // We will process the pits iteratively with increasing neighbourhood size. This is //
+ // for greater efficiency. Most of the depressions are likely to be solved with //
+ // neighbourhoods much smaller than the maximum. //
+ //////////////////////////////////////////////////////////////////////////////////////
+ let mut radius = 2; // the actual starting radius will be 4, which translates to a 9x9 filter for the first iteration.
+ if end_radius < radius {
+ end_radius = radius;
+ }
+ let mut iterations = vec![];
+ flag = true;
+ while flag {
+ radius *= 2;
+ if radius >= end_radius {
+ radius = end_radius;
+ flag = false;
+ }
+ iterations.push(radius);
+ }
+ let mut i = 1;
+ let num_iterations = iterations.len();
+ let mut undefined_flow_cells2 = vec![];
+ for neighbourhood_radius in iterations {
+ if verbose {
+ println!("Iteration {} of {} (search radius={} cells)", i, num_iterations, neighbourhood_radius);
+ }
+ i += 1;
+
+ ///////////////////////////////////////////////////////////////////////////////////
+ // Breach paths need to have a downward slope of at least min_val between cells. //
+ ///////////////////////////////////////////////////////////////////////////////////
+ let neighbourhood_size = 2 * neighbourhood_radius + 1;
+ let filter_size = (neighbourhood_size * neighbourhood_size) as usize;
+
+ //////////////////////////////////////////////////
+ // Find all cells with no downslope neighbours. //
+ //////////////////////////////////////////////////
+ if neighbourhood_radius == 4 {
+ let output2 = Arc::new(output);
+ let (tx, rx) = mpsc::channel();
+ for tid in 0..num_procs {
+ let output2 = output2.clone();
+ let tx = tx.clone();
+ thread::spawn(move || {
+ let mut z: f64;
+ let mut zn: f64;
+ let mut flag: bool;
+ for row in (1..rows-1).filter(|r| r % num_procs == tid) {
+ let mut pits = vec![];
+ for col in 1..columns-1 {
+ z = output2.get_value(row, col);
+ if z != nodata {
+ flag = true;
+ for n in 0..8 {
+ zn = output2.get_value(row + dy[n], col + dx[n]);
+ if zn < z || zn == nodata { // It either has a lower neighbour or is an edge cell.
+ flag = false;
+ break;
+ }
+ }
+ if flag { // it's a cell with undefined flow
+ pits.push((row, col, z));
+ }
+ }
+ }
+ tx.send(pits).unwrap();
+ }
+ });
+ }
+
+ for r in 0..rows-2 {
+ let mut pits = rx.recv().unwrap();
+ undefined_flow_cells2.append(&mut pits);
+
+ if verbose {
+ progress = (100.0_f64 * r as f64 / (rows - 3) as f64) as usize;
+ if progress != old_progress {
+ println!("Finding pits: {}%", progress);
+ old_progress = progress;
+ }
+ }
+ }
+
+ output = match Arc::try_unwrap(output2) {
+ Ok(val) => val,
+ Err(_) => panic!("Error unwrapping 'output'"),
+ };
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////
+ // We need to visit and (potentially) solve each undefined-flow cell in order from lowest //
+ // to highest. This is because some higher pits can be solved, or partially solved using //
+ // the breach paths of lower pits. //
+ ////////////////////////////////////////////////////////////////////////////////////////////
+ let mut undefined_flow_cells = undefined_flow_cells2.clone();
+ undefined_flow_cells2.clear();
+ /* Vec is a stack and so if we want to pop the values from lowest to highest, we need to sort
+ them from highest to lowest. */
+ undefined_flow_cells.sort_by(|a, b| b.2.partial_cmp(&a.2).unwrap_or(Equal));
+ let num_deps = undefined_flow_cells.len();
+ if num_deps == 0 {
+ if verbose {
+ println!("All depressions solved. Process ending...");
+ break;
+ }
+ }
+ if verbose && neighbourhood_radius == 4 {
+ println!("Num. pits: {}", num_deps);
+ }
+ num_solved = 0;
+ num_unsolved = 0;
+ let backlink_dir = [4i8, 5, 6, 7, 0, 1, 2, 3];
+ while let Some(cell) = undefined_flow_cells.pop() {
+ row = cell.0;
+ col = cell.1;
+ z = output.get_value(row, col);
+
+ // Is it still a pit cell? It may have been solved during a previous depression solution.
+ flag = true;
+ for n in 0..8 {
+ zn = output.get_value(row + dy[n], col + dx[n]);
+ if zn < z && zn != nodata { // It has a lower non-nodata cell
+ // Resolving some other pit cell resulted in a solution for this one.
+ num_solved += 1;
+ overall_num_solved += 1;
+ flag = false;
+ break;
+ }
+ }
+ if flag {
+ // Perform the cost-accumulation operation.
+ let mut minheap = BinaryHeap::with_capacity(filter_size);
+ let mut backlink: Array2D = Array2D::new(neighbourhood_size, neighbourhood_size, -1, -2)?;
+ let mut encountered: Array2D = Array2D::new(neighbourhood_size, neighbourhood_size, 0, -1)?;
+ let mut path_length: Array2D = Array2D::new(neighbourhood_size, neighbourhood_size, 0, -1)?;
+ encountered.set_value(neighbourhood_radius, neighbourhood_radius, 1i8);
+ minheap.push(GridCell {
+ row: neighbourhood_radius,
+ column: neighbourhood_radius,
+ priority: 0f64,
+ });
+ flag = true;
+ while !minheap.is_empty() && flag {
+ let cell = minheap.pop().unwrap();
+ accum = cell.priority;
+ if accum > max_cost { // There isn't a breach channel cheap enough
+ undefined_flow_cells2.push((row, col, z)); // Add it to the list for the next iteration
+ num_unsolved += 1;
+ flag = false;
+ break;
+ }
+ length = path_length.get_value(cell.row, cell.column);
+ zn = output.get_value(row + (cell.row - neighbourhood_radius), col + (cell.column - neighbourhood_radius));
+ cost1 = zn - z + length as f64 * small_num;
+ for n in 0..8 {
+ cn = cell.column + dx[n];
+ rn = cell.row + dy[n];
+ if encountered.get_value(rn, cn) == 0i8 { // not yet encountered
+ length += 1;
+ path_length.set_value(rn, cn, length);
+ backlink.set_value(rn, cn, backlink_dir[n]);
+ zn = output.get_value(row + (rn - neighbourhood_radius), col + (cn - neighbourhood_radius));
+ zout = z - (length as f64 * small_num);
+ if zn > zout {
+ cost2 = zn - zout;
+ new_cost = if minimize_dist {
+ accum + (cost1 + cost2) / 2f64 * cost_dist[n]
+ } else {
+ accum + cost2
+ };
+ encountered.set_value(rn, cn, 1i8);
+ minheap.push(GridCell {
+ row: rn,
+ column: cn,
+ priority: new_cost,
+ });
+ } else {
+ // We're at a cell that we can breach to
+ col += cn - neighbourhood_radius;
+ row += rn - neighbourhood_radius;
+ while flag {
+ // Find which cell to go to from here
+ if backlink.get_value(rn, cn) > -1i8 {
+ b = backlink.get_value(rn, cn) as usize;
+ cn += dx[b];
+ rn += dy[b];
+ col += dx[b];
+ row += dy[b];
+ zn = output.get_value(row, col);
+ length = path_length.get_value(rn, cn);
+ zout = z - (length as f64 * small_num);
+ if zn > zout {
+ output.set_value(row, col, zout);
+ }
+ } else {
+ flag = false;
+ }
+ }
+ num_solved += 1;
+ overall_num_solved += 1;
+
+ break; // don't check any more neighbours.
+ }
+ }
+ }
+ }
+
+ if flag { // Didn't find any lower cells.
+ undefined_flow_cells2.push((row, col, z)); // Add it to the list for the next iteration
+ num_unsolved += 1;
+ }
+ }
+
+ if verbose {
+ progress = (100.0_f64 * (1f64 - (undefined_flow_cells.len()) as f64 / (num_deps - 1) as f64)) as usize;
+ if progress != old_progress {
+ println!("Breaching: {}%", progress);
+ old_progress = progress;
+ }
+ }
+ }
+ if verbose {
+ println!("Num. solved pits: {}", num_solved);
+ println!("Num. unsolved pits: {}", num_unsolved);
+ }
+ }
+
+ if overall_num_solved > 0 && verbose {
+ println!("Overall num. solved pits: {}", overall_num_solved);
+ println!("Overall num. unsolved pits: {}", num_unsolved);
+ } else if verbose{
+ println!("No depressions found.");
+ }
+
+ if fill_deps && num_unsolved > 0 {
+ if verbose {
+ println!("Filling remaining depressions...");
+ }
+ // Find pit cells. This step is parallelized.
+ let output2 = Arc::new(output);
+ let (tx, rx) = mpsc::channel();
+ for tid in 0..num_procs {
+ let output2 = output2.clone();
+ let tx = tx.clone();
+ thread::spawn(move || {
+ let mut z: f64;
+ let mut zn: f64;
+ let mut flag: bool;
+ let mut pits = vec![];
+ for row in (1..rows-1).filter(|r| r % num_procs == tid) {
+ for col in 1..columns-1 {
+ z = output2.get_value(row, col);
+ if z != nodata {
+ flag = true;
+ for n in 0..8 {
+ zn = output2.get_value(row + dy[n], col + dx[n]);
+ if zn < z || zn == nodata { // It either has a lower neighbour or is an edge cell.
+ flag = false;
+ break;
+ }
+ }
+ if flag { // it's a cell with undefined flow
+ pits.push((row, col, z));
+ }
+ }
+ }
+ }
+ tx.send(pits).unwrap();
+ });
+ }
+
+ let mut undefined_flow_cells = vec![];
+ for p in 0..num_procs {
+ let mut pits = rx.recv().unwrap();
+ undefined_flow_cells.append(&mut pits);
+
+ if verbose {
+ progress = (100.0_f64 * (p + 1) as f64 / num_procs as f64) as usize;
+ if progress != old_progress {
+ println!("Finding pit cells: {}%", progress);
+ old_progress = progress;
+ }
+ }
+ }
+
+ output = match Arc::try_unwrap(output2) {
+ Ok(val) => val,
+ Err(_) => panic!("Error unwrapping 'output'"),
+ };
+
+ let num_deps = undefined_flow_cells.len();
+
+ // Now we need to perform an in-place depression filling
+ let mut minheap = BinaryHeap::new();
+ let mut visited: Array2D = Array2D::new(rows, columns, 0, -1)?;
+ let mut flats: Array2D = Array2D::new(rows, columns, 0, -1)?;
+ let mut possible_outlets = vec![];
+ // solve from highest to lowest
+ undefined_flow_cells.sort_by(|a, b| a.2.partial_cmp(&b.2).unwrap_or(Equal));
+ let mut pit_id = 1;
+ let mut flag: bool;
+ while let Some(cell) = undefined_flow_cells.pop() {
+ row = cell.0;
+ col = cell.1;
+ if flats.get_value(row, col) != 1 { // if it's already in a solved site, don't do it a second time.
+ // First there is a priority region-growing operation to find the outlets.
+ z = output.get_value(row, col);
+ minheap.clear();
+ minheap.push(GridCell {
+ row: row,
+ column: col,
+ priority: z,
+ });
+ visited.set_value(row, col, 1);
+ let mut outlet_found = false;
+ let mut outlet_z = f64::INFINITY;
+ let mut queue = VecDeque::new();
+ while let Some(cell2) = minheap.pop() {
+ z = cell2.priority;
+ if outlet_found && z > outlet_z {
+ break;
+ }
+ if !outlet_found {
+ for n in 0..8 {
+ cn = cell2.column + dx[n];
+ rn = cell2.row + dy[n];
+ if visited.get_value(rn, cn) == 0 {
+ zn = output.get_value(rn, cn);
+ if !outlet_found {
+ if zn >= z && zn != nodata {
+ minheap.push(GridCell {
+ row: rn,
+ column: cn,
+ priority: zn,
+ });
+ visited.set_value(rn, cn, 1);
+ } else if zn != nodata { // zn < z
+ // 'cell' has a lower neighbour that hasn't already passed through minheap.
+ // Therefore, 'cell' is a pour point cell.
+ outlet_found = true;
+ outlet_z = z;
+ queue.push_back((cell2.row, cell2.column));
+ possible_outlets.push((cell2.row, cell2.column));
+ }
+ } else if zn == outlet_z { // We've found the outlet but are still looking for additional outlets.
+ minheap.push(GridCell {
+ row: rn,
+ column: cn,
+ priority: zn,
+ });
+ visited.set_value(rn, cn, 1);
+ }
+ }
+ }
+ } else {
+ if z == outlet_z {
+ flag = false;
+ for n in 0..8 {
+ cn = cell2.column + dx[n];
+ rn = cell2.row + dy[n];
+ if visited.get_value(rn, cn) == 0 {
+ zn = output.get_value(rn, cn);
+ if zn < z {
+ flag = true;
+ } else if zn == outlet_z {
+ minheap.push(GridCell {
+ row: rn,
+ column: cn,
+ priority: zn,
+ });
+ visited.set_value(rn, cn, 1);
+ }
+ }
+ }
+ if flag { // it's an outlet
+ queue.push_back((cell2.row, cell2.column));
+ possible_outlets.push((cell2.row, cell2.column));
+ } else {
+ visited.set_value(cell2.row, cell2.column, 1);
+ }
+ }
+ }
+ }
+
+ // Now that we have the outlets, raise the interior of the depression
+ if outlet_found {
+ while let Some(cell2) = queue.pop_front() {
+ for n in 0..8 {
+ rn = cell2.0 + dy[n];
+ cn = cell2.1 + dx[n];
+ if visited.get_value(rn, cn) == 1 {
+ visited.set_value(rn, cn, 0);
+ queue.push_back((rn, cn));
+ z = output.get_value(rn, cn);
+ if z < outlet_z {
+ output.set_value(rn, cn, outlet_z);
+ flats.set_value(rn, cn, 1);
+ } else if z == outlet_z {
+ flats.set_value(rn, cn, 1);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if verbose {
+ progress = (100.0_f64 * pit_id as f64 / num_deps as f64) as usize;
+ if progress != old_progress {
+ println!("Filling depressions: {}%", progress);
+ old_progress = progress;
+ }
+ }
+ pit_id += 1;
+ }
+
+ drop(visited);
+
+ if small_num > 0f64 { // fix the flats
+ if verbose {
+ println!("Fixing flow on flats...");
+ println!("Flats increment value: {}", small_num);
+ }
+ // Some of the potential outlets really will have lower cells.
+ // let mut queue = VecDeque::new();
+ minheap.clear();
+ while let Some(cell) = possible_outlets.pop() {
+ z = output.get_value(cell.0, cell.1);
+ flag = false;
+ for n in 0..8 {
+ rn = cell.0 + dy[n];
+ cn = cell.1 + dx[n];
+ zn = output.get_value(rn, cn);
+ if zn < z && zn != nodata {
+ flag = true;
+ break;
+ }
+ }
+ if flag { // it's confirmed as an outlet
+ minheap.push(GridCell {
+ row: cell.0,
+ column: cell.1,
+ priority: z,
+ });
+ }
+ }
+
+ let num_outlets = minheap.len();
+
+ while let Some(cell) = minheap.pop() {
+ if flats.get_value(cell.row, cell.column) != 3 {
+ z = output.get_value(cell.row, cell.column);
+ flats.set_value(cell.row, cell.column, 3);
+ let mut outlets = vec![];
+ outlets.push(cell);
+ // Are there any other outlet cells at the same elevation (likely for the same feature)
+ flag = true;
+ while flag {
+ match minheap.peek() {
+ Some(cell2) => {
+ if cell2.priority == z {
+ flats.set_value(cell2.row, cell2.column, 3);
+ outlets.push(minheap.pop().unwrap());
+ } else {
+ flag = false;
+ }
+ },
+ None => {
+ flag = false;
+ }
+ }
+
+ }
+ // let mut queue = VecDeque::new();
+ let mut minheap2 = BinaryHeap::new();
+ for cell2 in &outlets {
+ z = output.get_value(cell2.row, cell2.column);
+ for n in 0..8 {
+ rn = cell2.row + dy[n];
+ cn = cell2.column + dx[n];
+ if flats.get_value(rn, cn) != 3 {
+ zn = output.get_value(rn, cn);
+ if zn == z && zn != nodata {
+ // queue.push_back((rn, cn, z));
+ minheap2.push(GridCell2 {
+ row: rn,
+ column: cn,
+ z: z,
+ priority: input.get_value(rn, cn),
+ });
+ output.set_value(rn, cn, z + small_num);
+ flats.set_value(rn, cn, 3);
+ }
+ }
+ }
+ }
+ // Now fix the flats
+ while let Some(cell2) = minheap2.pop() {
+ z = output.get_value(cell2.row, cell2.column);
+ for n in 0..8 {
+ rn = cell2.row + dy[n];
+ cn = cell2.column + dx[n];
+ if flats.get_value(rn, cn) != 3 {
+ zn = output.get_value(rn, cn);
+ if zn < z + small_num && zn >= cell2.z && zn != nodata {
+ // queue.push_back((rn, cn, cell2.2));
+ minheap2.push(GridCell2 {
+ row: rn,
+ column: cn,
+ z: cell2.z,
+ priority: input.get_value(rn, cn),
+ });
+ output.set_value(rn, cn, z + small_num);
+ flats.set_value(rn, cn, 3);
+ }
+ }
+ }
+ }
+ }
+
+ if verbose {
+ progress = (100.0_f64 * (1f64 - minheap.len() as f64 / num_outlets as f64)) as usize;
+ if progress != old_progress {
+ println!("Fixing flats: {}%", progress);
+ old_progress = progress;
+ }
+ }
+ }
+ }
+ }
+
+
+ // // Now we need to perform an in-place depression filling
+ // let mut minheap = BinaryHeap::new();
+ // let mut visited: Array2D = Array2D::new(rows, columns, 0, -1)?;
+ // let mut outlet_visited: Array2D = Array2D::new(rows, columns, 0, -1)?;
+ // undefined_flow_cells2.sort_by(|a, b| a.2.partial_cmp(&b.2).unwrap_or(Equal));
+ // let num_deps = undefined_flow_cells2.len();
+ // let mut pit_id = 1;
+ // let mut num_dep_cells: usize;
+ // while !undefined_flow_cells2.is_empty() {
+ // let cell = undefined_flow_cells2.pop().unwrap();
+ // row = cell.0;
+ // col = cell.1;
+ // if outlet_visited.get_value(row, col) != 1 { // if it's already in a solved site, don't do it a second time.
+ // z = output.get_value(row, col);
+ // minheap.clear();
+ // minheap.push(GridCell {
+ // row: row,
+ // column: col,
+ // priority: z,
+ // });
+ // visited.set_value(row, col, 1);
+ // num_dep_cells = 1;
+ // flag = true;
+ // while !minheap.is_empty() && flag {
+ // let cell = minheap.pop().unwrap();
+ // z = cell.priority;
+ // for n in 0..8 {
+ // cn = cell.column + dx[n];
+ // rn = cell.row + dy[n];
+ // if visited.get_value(rn, cn) == 0 {
+ // zn = output.get_value(rn, cn);
+ // if zn >= z {
+ // minheap.push(GridCell {
+ // row: rn,
+ // column: cn,
+ // priority: zn,
+ // });
+ // visited.set_value(rn, cn, 1);
+ // num_dep_cells += 1;
+ // } else {
+ // // 'cell' has a lower neighbour that hasn't already passed through minheap.
+ // // 'cell' therefore is a pour point cell.
+ // visited.set_value(cell.row, cell.column, 0);
+ // outlet_visited.set_value(cell.row, cell.column, 1);
+ // let mut queue = VecDeque::with_capacity(num_dep_cells);
+ // queue.push_back((cell.row, cell.column));
+ // while let Some(cell2) = queue.pop_front() {
+ // z = output.get_value(cell2.0, cell2.1);
+ // for n in 0..8 {
+ // rn = cell2.0 + dy[n];
+ // cn = cell2.1 + dx[n];
+ // if visited.get_value(rn, cn) == 1 {
+ // visited.set_value(rn, cn, 0);
+ // zn = output.get_value(rn, cn);
+ // if zn < z + small_num {
+ // queue.push_back((rn, cn));
+ // output.set_value(rn, cn, z + small_num);
+ // outlet_visited.set_value(rn, cn, 1);
+ // }
+ // }
+ // }
+ // }
+ // flag = false;
+ // break;
+ // }
+ // }
+ // }
+ // }
+ // }
+
+ // if verbose {
+ // progress = (100.0_f64 * pit_id as f64 / num_deps as f64) as usize;
+ // if progress != old_progress {
+ // println!("Filling remaining depressions: {}%", progress);
+ // old_progress = progress;
+ // }
+ // }
+ // pit_id += 1;
+ // }
+
+
+
+ // // Start by finding all cells that neighbour NoData cells.
+ // let mut visited: Array2D = Array2D::new(rows, columns, 0, -1)?;
+ // let mut minheap = BinaryHeap::with_capacity((rows * columns) as usize);
+ // let mut num_cells_visited = 0;
+ // for row in 0..rows {
+ // for col in 0..columns {
+ // z = output.get_value(row, col);
+ // if z != nodata {
+ // for n in 0..8 {
+ // if output.get_value(row + dy[n], col + dx[n]) == nodata {
+ // minheap.push(GridCell {
+ // row: row,
+ // column: col,
+ // priority: z,
+ // });
+ // visited.set_value(row, col, 1);
+ // num_cells_visited += 1;
+ // break;
+ // }
+ // }
+ // } else {
+ // visited.set_value(row, col, 1);
+ // num_cells_visited += 1;
+ // }
+ // }
+ // if verbose {
+ // progress = (100.0_f64 * row as f64 / (rows - 1) as f64) as usize;
+ // if progress != old_progress {
+ // println!("Finding edges: {}%", progress);
+ // old_progress = progress;
+ // }
+ // }
+ // }
+
+ // while !minheap.is_empty() {
+ // let cell = minheap.pop().unwrap();
+ // row = cell.row;
+ // col = cell.column;
+ // z = output.get_value(row, col);
+ // for n in 0..8 {
+ // rn = row + dy[n];
+ // cn = col + dx[n];
+ // if visited.get_value(rn, cn) == 0i8 {
+ // zn = output.get_value(rn, cn);
+ // if zn < z + small_num {
+ // zn = z + small_num;
+ // output.set_value(rn, cn, zn);
+ // }
+ // minheap.push(GridCell {
+ // row: rn,
+ // column: cn,
+ // priority: zn,
+ // });
+ // visited.set_value(rn, cn, 1);
+ // num_cells_visited += 1;
+ // }
+ // }
+ // if verbose {
+ // progress = (100.0_f64 * num_cells_visited as f64 / (rows*columns - 1) as f64) as usize;
+ // if progress != old_progress {
+ // println!("Filling: {}%", progress);
+ // old_progress = progress;
+ // }
+ // }
+ // }
+
+
+ let elapsed_time = get_formatted_elapsed_time(start);
+ output.configs.display_min = display_min;
+ output.configs.display_max = display_max;
+ output.add_metadata_entry(format!(
+ "Created by whitebox_tools\' {} tool",
+ self.get_tool_name()
+ ));
+ output.add_metadata_entry(format!("Input file: {}", input_file));
+ output.add_metadata_entry(format!("Maximum search window radius: {}", end_radius));
+ output.add_metadata_entry(format!("Maximum breach cost: {}", max_cost));
+ output.add_metadata_entry(format!("Flat elevation increment: {}", small_num));
+ output.add_metadata_entry(format!("Remaining depressions filled: {}", fill_deps));
+ output.add_metadata_entry(format!("Elapsed Time (excluding I/O): {}", elapsed_time));
+
+ if verbose {
+ println!("Saving data...")
+ };
+ let _ = match output.write() {
+ Ok(_) => {
+ if verbose {
+ println!("Output file written")
+ }
+ }
+ Err(e) => return Err(e),
+ };
+ if verbose {
+ println!(
+ "{}",
+ &format!("Elapsed Time (excluding I/O): {}", elapsed_time)
+ );
+ }
+
+ Ok(())
+ }
+}
+
+#[derive(PartialEq, Debug)]
+struct GridCell {
+ row: isize,
+ column: isize,
+ priority: f64,
+}
+
+impl Eq for GridCell {}
+
+impl PartialOrd for GridCell {
+ fn partial_cmp(&self, other: &Self) -> Option {
+ other.priority.partial_cmp(&self.priority)
+ }
+}
+
+impl Ord for GridCell {
+ fn cmp(&self, other: &GridCell) -> Ordering {
+ self.partial_cmp(other).unwrap()
+ }
+}
+
+#[derive(PartialEq, Debug)]
+struct GridCell2 {
+ row: isize,
+ column: isize,
+ z: f64,
+ priority: f64,
+}
+
+impl Eq for GridCell2 {}
+
+impl PartialOrd for GridCell2 {
+ fn partial_cmp(&self, other: &Self) -> Option {
+ other.priority.partial_cmp(&self.priority)
+ }
+}
+
+impl Ord for GridCell2 {
+ fn cmp(&self, other: &Self) -> Ordering {
+ self.partial_cmp(other).unwrap()
+ }
+}
diff --git a/src/tools/hydro_analysis/breach_pits.rs b/src/tools/hydro_analysis/breach_pits.rs
index 96fae2d39..ac7754340 100644
--- a/src/tools/hydro_analysis/breach_pits.rs
+++ b/src/tools/hydro_analysis/breach_pits.rs
@@ -124,7 +124,7 @@ impl WhiteboxTool for BreachSingleCellPits {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/hydro_analysis/burn_streams_at_roads.rs b/src/tools/hydro_analysis/burn_streams_at_roads.rs
index a049b7ffc..ce30e9447 100644
--- a/src/tools/hydro_analysis/burn_streams_at_roads.rs
+++ b/src/tools/hydro_analysis/burn_streams_at_roads.rs
@@ -44,7 +44,7 @@ impl BurnStreamsAtRoads {
// public constructor
let name = "BurnStreamsAtRoads".to_string();
let toolbox = "Hydrological Analysis".to_string();
- let description = "Rasterizes vector streams based on Lindsay (2016) method.".to_string();
+ let description = "Burns-in streams at the sites of road embankments.".to_string();
let mut parameters = vec![];
parameters.push(ToolParameter {
@@ -169,7 +169,7 @@ impl WhiteboxTool for BurnStreamsAtRoads {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/hydro_analysis/d8_flow_accum.rs b/src/tools/hydro_analysis/d8_flow_accum.rs
index 29ddb90a9..38cd52125 100644
--- a/src/tools/hydro_analysis/d8_flow_accum.rs
+++ b/src/tools/hydro_analysis/d8_flow_accum.rs
@@ -172,7 +172,7 @@ impl WhiteboxTool for D8FlowAccumulation {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/hydro_analysis/d8_mass_flux.rs b/src/tools/hydro_analysis/d8_mass_flux.rs
index a56d6b62d..a9abaf142 100644
--- a/src/tools/hydro_analysis/d8_mass_flux.rs
+++ b/src/tools/hydro_analysis/d8_mass_flux.rs
@@ -168,7 +168,7 @@ impl WhiteboxTool for D8MassFlux {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/hydro_analysis/d8_pointer.rs b/src/tools/hydro_analysis/d8_pointer.rs
index 72d5086e4..d6ede4fe4 100644
--- a/src/tools/hydro_analysis/d8_pointer.rs
+++ b/src/tools/hydro_analysis/d8_pointer.rs
@@ -155,7 +155,7 @@ impl WhiteboxTool for D8Pointer {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/hydro_analysis/depth_in_sink.rs b/src/tools/hydro_analysis/depth_in_sink.rs
index 0f1f9b050..44fc08b0a 100644
--- a/src/tools/hydro_analysis/depth_in_sink.rs
+++ b/src/tools/hydro_analysis/depth_in_sink.rs
@@ -2,20 +2,23 @@
This tool is part of the WhiteboxTools geospatial analysis library.
Authors: Dr. John Lindsay
Created: 11/07/2017
-Last Modified: 18/10/2019
+Last Modified: 05/12/2019
License: MIT
*/
use crate::raster::*;
use crate::tools::*;
+use crate::structures::Array2D;
use std::cmp::Ordering;
-use std::collections::BinaryHeap;
-use std::collections::VecDeque;
+use std::cmp::Ordering::Equal;
+use std::collections::{BinaryHeap, VecDeque};
use std::env;
use std::f64;
-use std::i32;
use std::io::{Error, ErrorKind};
use std::path;
+use std::sync::mpsc;
+use std::sync::Arc;
+use std::thread;
/// This tool measures the depth that each grid cell in an input (`--dem`) raster digital elevation model (DEM)
/// lies within a sink feature, i.e. a closed topographic depression. A sink, or depression, is a bowl-like
@@ -142,7 +145,7 @@ impl WhiteboxTool for DepthInSink {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
@@ -154,31 +157,25 @@ impl WhiteboxTool for DepthInSink {
if vec.len() > 1 {
keyval = true;
}
- if vec[0].to_lowercase() == "-i"
- || vec[0].to_lowercase() == "--input"
- || vec[0].to_lowercase() == "--dem"
- {
- if keyval {
- input_file = vec[1].to_string();
+ let flag_val = vec[0].to_lowercase().replace("--", "-");
+ if flag_val == "-i" || flag_val == "-input" || flag_val == "-dem" {
+ input_file = if keyval {
+ vec[1].to_string()
} else {
- input_file = args[i + 1].to_string();
- }
- } else if vec[0].to_lowercase() == "-o" || vec[0].to_lowercase() == "--output" {
- if keyval {
- output_file = vec[1].to_string();
+ args[i + 1].to_string()
+ };
+ } else if flag_val == "-o" || flag_val == "-output" {
+ output_file = if keyval {
+ vec[1].to_string()
} else {
- output_file = args[i + 1].to_string();
- }
- } else if vec[0].to_lowercase() == "-zero_background"
- || vec[0].to_lowercase() == "--zero_background"
- || vec[0].to_lowercase() == "--esri_style"
- {
+ args[i + 1].to_string()
+ };
+ } else if flag_val == "-zero_background" {
if vec.len() == 1 || !vec[1].to_string().to_lowercase().contains("false") {
zero_background = true;
}
}
}
-
if verbose {
println!("***************{}", "*".repeat(self.get_tool_name().len()));
println!("* Welcome to {} *", self.get_tool_name());
@@ -206,158 +203,239 @@ impl WhiteboxTool for DepthInSink {
let start = Instant::now();
let rows = input.configs.rows as isize;
let columns = input.configs.columns as isize;
- let num_cells = rows * columns;
let nodata = input.configs.nodata;
- let mut output = Raster::initialize_using_file(&output_file, &input);
- let mut background_val = (i32::min_value() + 1) as f64;
- output.reinitialize_values(background_val);
-
- /*
- Find the data edges. This is complicated by the fact that DEMs frequently
- have nodata edges, whereby the DEM does not occupy the full extent of
- the raster. One approach to doing this would be simply to scan the
- raster, looking for cells that neighbour nodata values. However, this
- assumes that there are no interior nodata holes in the dataset. Instead,
- the approach used here is to perform a region-growing operation, looking
- for nodata values along the raster's edges.
- */
-
- let mut queue: VecDeque<(isize, isize)> =
- VecDeque::with_capacity((rows * columns) as usize);
- for row in 0..rows {
- /*
- Note that this is only possible because Whitebox rasters
- allow you to address cells beyond the raster extent but
- return the nodata value for these regions.
- */
- queue.push_back((row, -1));
- queue.push_back((row, columns));
- }
-
- for col in 0..columns {
- queue.push_back((-1, col));
- queue.push_back((rows, col));
- }
+ let mut filled_dem = input.get_data_as_array2d();
- /*
- minheap is the priority queue. Note that I've tested using integer-based
- priority values, by multiplying the elevations, but this didn't result
- in a significant performance gain over the use of f64s.
- */
- let mut minheap = BinaryHeap::with_capacity((rows * columns) as usize);
- let mut num_solved_cells = 0;
- let mut zin_n: f64; // value of neighbour of row, col in input raster
- let mut zout: f64; // value of row, col in output raster
- let mut zout_n: f64; // value of neighbour of row, col in output raster
+ let (mut col, mut row): (isize, isize);
+ let (mut rn, mut cn): (isize, isize);
+ let (mut z, mut zn): (f64, f64);
let dx = [1, 1, 1, 0, -1, -1, -1, 0];
let dy = [-1, 0, 1, 1, 1, 0, -1, -1];
- let (mut row, mut col): (isize, isize);
- let (mut row_n, mut col_n): (isize, isize);
- while !queue.is_empty() {
- let cell = queue.pop_front().unwrap();
- row = cell.0;
- col = cell.1;
- for n in 0..8 {
- row_n = row + dy[n];
- col_n = col + dx[n];
- zin_n = input[(row_n, col_n)];
- zout_n = output[(row_n, col_n)];
- if zout_n == background_val {
- if zin_n == nodata {
- output[(row_n, col_n)] = nodata;
- queue.push_back((row_n, col_n));
- } else {
- output[(row_n, col_n)] = zin_n;
- // Push it onto the priority queue for the priority flood operation
- minheap.push(GridCell {
- row: row_n,
- column: col_n,
- priority: zin_n,
- });
+
+ // Find pit cells. This step is parallelized.
+ let num_procs = num_cpus::get() as isize;
+ let filled_dem2 = Arc::new(filled_dem);
+ let (tx, rx) = mpsc::channel();
+ for tid in 0..num_procs {
+ let filled_dem2 = filled_dem2.clone();
+ let tx = tx.clone();
+ thread::spawn(move || {
+ let mut z: f64;
+ let mut zn: f64;
+ let mut flag: bool;
+ let mut pits = vec![];
+ for row in (1..rows-1).filter(|r| r % num_procs == tid) {
+ for col in 1..columns-1 {
+ z = filled_dem2.get_value(row, col);
+ if z != nodata {
+ flag = true;
+ for n in 0..8 {
+ zn = filled_dem2.get_value(row + dy[n], col + dx[n]);
+ if zn < z || zn == nodata { // It either has a lower neighbour or is an edge cell.
+ flag = false;
+ break;
+ }
+ }
+ if flag { // it's a cell with undefined flow
+ pits.push((row, col, z));
+ }
+ }
}
- num_solved_cells += 1;
}
- }
+ tx.send(pits).unwrap();
+ });
+ }
+ let mut undefined_flow_cells = vec![];
+ for p in 0..num_procs {
+ let mut pits = rx.recv().unwrap();
+ undefined_flow_cells.append(&mut pits);
+
if verbose {
- progress = (100.0_f64 * num_solved_cells as f64 / (num_cells - 1) as f64) as usize;
+ progress = (100.0_f64 * (p + 1) as f64 / num_procs as f64) as usize;
if progress != old_progress {
- println!("progress: {}%", progress);
+ println!("Finding pit cells: {}%", progress);
old_progress = progress;
}
}
}
- // Perform the priority flood operation.
- while !minheap.is_empty() {
- let cell = minheap.pop().unwrap();
- row = cell.row;
- col = cell.column;
- zout = output[(row, col)];
- for n in 0..8 {
- row_n = row + dy[n];
- col_n = col + dx[n];
- zout_n = output[(row_n, col_n)];
- if zout_n == background_val {
- zin_n = input[(row_n, col_n)];
- if zin_n != nodata {
- if zin_n < zout {
- zin_n = zout;
- } // We're in a depression. Raise the elevation.
- output[(row_n, col_n)] = zin_n;
- minheap.push(GridCell {
- row: row_n,
- column: col_n,
- priority: zin_n,
- });
+ let mut input_configs = input.configs.clone();
+
+ filled_dem = match Arc::try_unwrap(filled_dem2) {
+ Ok(val) => val,
+ Err(_) => panic!("Error unwrapping 'filled_dem'"),
+ };
+
+ let num_deps = undefined_flow_cells.len();
+
+ // Now we need to perform an in-place depression filling
+ let mut minheap = BinaryHeap::new();
+ let mut visited: Array2D = Array2D::new(rows, columns, 0, -1)?;
+ let mut flats: Array2D = Array2D::new(rows, columns, 0, -1)?;
+ let mut possible_outlets = vec![];
+ // solve from highest to lowest
+ undefined_flow_cells.sort_by(|a, b| a.2.partial_cmp(&b.2).unwrap_or(Equal));
+ let mut pit_id = 1;
+ let mut flag: bool;
+ while let Some(cell) = undefined_flow_cells.pop() {
+ row = cell.0;
+ col = cell.1;
+ if flats.get_value(row, col) != 1 { // if it's already in a solved site, don't do it a second time.
+ // First there is a priority region-growing operation to find the outlets.
+ z = filled_dem.get_value(row, col);
+ minheap.clear();
+ minheap.push(GridCell {
+ row: row,
+ column: col,
+ priority: z,
+ });
+ visited.set_value(row, col, 1);
+ let mut outlet_found = false;
+ let mut outlet_z = f64::INFINITY;
+ let mut queue = VecDeque::new();
+ while let Some(cell2) = minheap.pop() {
+ z = cell2.priority;
+ if outlet_found && z > outlet_z {
+ break;
+ }
+ if !outlet_found {
+ for n in 0..8 {
+ cn = cell2.column + dx[n];
+ rn = cell2.row + dy[n];
+ if visited.get_value(rn, cn) == 0 {
+ zn = filled_dem.get_value(rn, cn);
+ if !outlet_found {
+ if zn >= z && zn != nodata {
+ minheap.push(GridCell {
+ row: rn,
+ column: cn,
+ priority: zn,
+ });
+ visited.set_value(rn, cn, 1);
+ } else if zn != nodata { // zn < z
+ // 'cell' has a lower neighbour that hasn't already passed through minheap.
+ // Therefore, 'cell' is a pour point cell.
+ outlet_found = true;
+ outlet_z = z;
+ queue.push_back((cell2.row, cell2.column));
+ possible_outlets.push((cell2.row, cell2.column));
+ }
+ } else if zn == outlet_z { // We've found the outlet but are still looking for additional outlets.
+ minheap.push(GridCell {
+ row: rn,
+ column: cn,
+ priority: zn,
+ });
+ visited.set_value(rn, cn, 1);
+ }
+ }
+ }
} else {
- // Interior nodata cells are still treated as nodata and are not filled.
- output[(row_n, col_n)] = nodata;
- num_solved_cells += 1;
+ if z == outlet_z {
+ flag = false;
+ for n in 0..8 {
+ cn = cell2.column + dx[n];
+ rn = cell2.row + dy[n];
+ if visited.get_value(rn, cn) == 0 {
+ zn = filled_dem.get_value(rn, cn);
+ if zn < z {
+ flag = true;
+ } else if zn == outlet_z {
+ minheap.push(GridCell {
+ row: rn,
+ column: cn,
+ priority: zn,
+ });
+ visited.set_value(rn, cn, 1);
+ }
+ }
+ }
+ if flag { // it's an outlet
+ queue.push_back((cell2.row, cell2.column));
+ possible_outlets.push((cell2.row, cell2.column));
+ } else {
+ visited.set_value(cell2.row, cell2.column, 1);
+ }
+ }
+ }
+ }
+
+ // Now that we have the outlets, raise the interior of the depression
+ if outlet_found {
+ while let Some(cell2) = queue.pop_front() {
+ for n in 0..8 {
+ rn = cell2.0 + dy[n];
+ cn = cell2.1 + dx[n];
+ if visited.get_value(rn, cn) == 1 {
+ visited.set_value(rn, cn, 0);
+ queue.push_back((rn, cn));
+ z = filled_dem.get_value(rn, cn);
+ if z < outlet_z {
+ filled_dem.set_value(rn, cn, outlet_z);
+ flats.set_value(rn, cn, 1);
+ } else if z == outlet_z {
+ flats.set_value(rn, cn, 1);
+ }
+ }
+ }
}
}
}
if verbose {
- num_solved_cells += 1;
- progress = (100.0_f64 * num_solved_cells as f64 / (num_cells - 1) as f64) as usize;
+ progress = (100.0_f64 * pit_id as f64 / num_deps as f64) as usize;
if progress != old_progress {
- println!("Progress (Loop 1 of 2): {}%", progress);
+ println!("Finding depressions: {}%", progress);
old_progress = progress;
}
}
+ pit_id += 1;
}
- background_val = nodata;
+ drop(visited);
+
+ input_configs.nodata = -32768f64;
+ let mut output = Raster::initialize_using_config(&output_file, &input_configs);
if zero_background {
- background_val = 0f64;
+ output.reinitialize_values(0f64);
}
- for row in 0..rows {
- for col in 0..columns {
- if output[(row, col)] > input[(row, col)] {
- output[(row, col)] = output[(row, col)] - input[(row, col)];
- } else {
- if input[(row, col)] != nodata {
- output[(row, col)] = background_val;
- } else {
- output[(row, col)] = nodata;
+ output.configs.data_type = DataType::F32;
+ let num_outlets = possible_outlets.len();
+ let mut diff: f64;
+ while let Some(cell) = possible_outlets.pop() {
+ if flats.get_value(cell.0, cell.1) == 1 {
+ z = filled_dem.get_value(cell.0, cell.1);
+ output.set_value(cell.0, cell.1, 0f64);
+ let mut queue = VecDeque::new();
+ flats.set_value(cell.0, cell.1, 0);
+ queue.push_back((cell.0, cell.1));
+ while let Some(cell2) = queue.pop_front() {
+ for n in 0..8 {
+ rn = cell2.0 + dy[n];
+ cn = cell2.1 + dx[n];
+ if flats.get_value(rn, cn) == 1 {
+ if filled_dem.get_value(rn, cn) == z {
+ flats.set_value(rn, cn, 0);
+ diff = z - input.get_value(rn, cn);
+ output.set_value(rn, cn, diff);
+ queue.push_back((rn, cn));
+ }
+ }
}
}
}
if verbose {
- progress = (100.0_f64 * row as f64 / (rows - 1) as f64) as usize;
+ progress = (100.0_f64 * (1.0 - possible_outlets.len() as f64 / num_outlets as f64)) as usize;
if progress != old_progress {
- println!("Progress (Loop 2 of 2): {}%", progress);
+ println!("Estimating depths: {}%", progress);
old_progress = progress;
}
}
}
let elapsed_time = get_formatted_elapsed_time(start);
- output.configs.data_type = DataType::F32;
- output.configs.palette = "qual.plt".to_string();
- output.configs.photometric_interp = PhotometricInterpretation::Categorical;
output.add_metadata_entry(format!(
"Created by whitebox_tools\' {} tool",
self.get_tool_name()
@@ -403,12 +481,423 @@ impl PartialOrd for GridCell {
}
impl Ord for GridCell {
- fn cmp(&self, other: &GridCell) -> Ordering {
- let ord = self.partial_cmp(other).unwrap();
- match ord {
- Ordering::Greater => Ordering::Less,
- Ordering::Less => Ordering::Greater,
- Ordering::Equal => ord,
- }
+ fn cmp(&self, other: &Self) -> Ordering {
+ self.partial_cmp(other).unwrap()
}
}
+
+
+// /*
+// This tool is part of the WhiteboxTools geospatial analysis library.
+// Authors: Dr. John Lindsay
+// Created: 11/07/2017
+// Last Modified: 18/10/2019
+// License: MIT
+// */
+
+// use crate::raster::*;
+// use crate::tools::*;
+// use std::cmp::Ordering;
+// use std::collections::BinaryHeap;
+// use std::collections::VecDeque;
+// use std::env;
+// use std::f64;
+// use std::i32;
+// use std::io::{Error, ErrorKind};
+// use std::path;
+
+// /// This tool measures the depth that each grid cell in an input (`--dem`) raster digital elevation model (DEM)
+// /// lies within a sink feature, i.e. a closed topographic depression. A sink, or depression, is a bowl-like
+// /// landscape feature, which is characterized by interior drainage and groundwater recharge. The `DepthInSink` tool
+// /// operates by differencing a filled DEM, using the same depression filling method as `FillDepressions`, and the
+// /// original surface model.
+// ///
+// /// In addition to the names of the input DEM (`--dem`) and the output raster (`--output`), the user must specify
+// /// whether the background value (i.e. the value assigned to grid cells that are not contained within sinks) should be
+// /// set to 0.0 (`--zero_background`) Without this optional parameter specified, the tool will use the NoData value
+// /// as the background value.
+// ///
+// /// # Reference
+// /// Antonić, O., Hatic, D., & Pernar, R. (2001). DEM-based depth in sink as an environmental estimator. Ecological
+// /// Modelling, 138(1-3), 247-254.
+// ///
+// /// # See Also
+// /// `FillDepressions`
+// pub struct DepthInSink {
+// name: String,
+// description: String,
+// toolbox: String,
+// parameters: Vec,
+// example_usage: String,
+// }
+
+// impl DepthInSink {
+// pub fn new() -> DepthInSink {
+// // public constructor
+// let name = "DepthInSink".to_string();
+// let toolbox = "Hydrological Analysis".to_string();
+// let description = "Measures the depth of sinks (depressions) in a DEM.".to_string();
+
+// let mut parameters = vec![];
+// parameters.push(ToolParameter {
+// name: "Input DEM File".to_owned(),
+// flags: vec!["-i".to_owned(), "--dem".to_owned()],
+// description: "Input raster DEM file.".to_owned(),
+// parameter_type: ParameterType::ExistingFile(ParameterFileType::Raster),
+// default_value: None,
+// optional: false,
+// });
+
+// parameters.push(ToolParameter {
+// name: "Output File".to_owned(),
+// flags: vec!["-o".to_owned(), "--output".to_owned()],
+// description: "Output raster file.".to_owned(),
+// parameter_type: ParameterType::NewFile(ParameterFileType::Raster),
+// default_value: None,
+// optional: false,
+// });
+
+// parameters.push(ToolParameter {
+// name: "Should a background value of zero be used?".to_owned(),
+// flags: vec!["--zero_background".to_owned()],
+// description: "Flag indicating whether the background value of zero should be used."
+// .to_owned(),
+// parameter_type: ParameterType::Boolean,
+// default_value: None,
+// optional: true,
+// });
+
+// let sep: String = path::MAIN_SEPARATOR.to_string();
+// let p = format!("{}", env::current_dir().unwrap().display());
+// let e = format!("{}", env::current_exe().unwrap().display());
+// let mut short_exe = e
+// .replace(&p, "")
+// .replace(".exe", "")
+// .replace(".", "")
+// .replace(&sep, "");
+// if e.contains(".exe") {
+// short_exe += ".exe";
+// }
+// let usage = format!(">>.*{0} -r={1} -v --wd=\"*path*to*data*\" --dem=DEM.tif -o=output.tif --zero_background", short_exe, name).replace("*", &sep);
+
+// DepthInSink {
+// name: name,
+// description: description,
+// toolbox: toolbox,
+// parameters: parameters,
+// example_usage: usage,
+// }
+// }
+// }
+
+// impl WhiteboxTool for DepthInSink {
+// fn get_source_file(&self) -> String {
+// String::from(file!())
+// }
+
+// fn get_tool_name(&self) -> String {
+// self.name.clone()
+// }
+
+// fn get_tool_description(&self) -> String {
+// self.description.clone()
+// }
+
+// fn get_tool_parameters(&self) -> String {
+// match serde_json::to_string(&self.parameters) {
+// Ok(json_str) => return format!("{{\"parameters\":{}}}", json_str),
+// Err(err) => return format!("{:?}", err),
+// }
+// }
+
+// fn get_example_usage(&self) -> String {
+// self.example_usage.clone()
+// }
+
+// fn get_toolbox(&self) -> String {
+// self.toolbox.clone()
+// }
+
+// fn run<'a>(
+// &self,
+// args: Vec,
+// working_directory: &'a str,
+// verbose: bool,
+// ) -> Result<(), Error> {
+// let mut input_file = String::new();
+// let mut output_file = String::new();
+// let mut zero_background = false;
+
+// if args.len() == 0 {
+// return Err(Error::new(
+// ErrorKind::InvalidInput,
+// "Tool run with no parameters.",
+// ));
+// }
+// for i in 0..args.len() {
+// let mut arg = args[i].replace("\"", "");
+// arg = arg.replace("\'", "");
+// let cmd = arg.split("="); // in case an equals sign was used
+// let vec = cmd.collect::>();
+// let mut keyval = false;
+// if vec.len() > 1 {
+// keyval = true;
+// }
+// if vec[0].to_lowercase() == "-i"
+// || vec[0].to_lowercase() == "--input"
+// || vec[0].to_lowercase() == "--dem"
+// {
+// if keyval {
+// input_file = vec[1].to_string();
+// } else {
+// input_file = args[i + 1].to_string();
+// }
+// } else if vec[0].to_lowercase() == "-o" || vec[0].to_lowercase() == "--output" {
+// if keyval {
+// output_file = vec[1].to_string();
+// } else {
+// output_file = args[i + 1].to_string();
+// }
+// } else if vec[0].to_lowercase() == "-zero_background"
+// || vec[0].to_lowercase() == "--zero_background"
+// || vec[0].to_lowercase() == "--esri_style"
+// {
+// if vec.len() == 1 || !vec[1].to_string().to_lowercase().contains("false") {
+// zero_background = true;
+// }
+// }
+// }
+
+// if verbose {
+// println!("***************{}", "*".repeat(self.get_tool_name().len()));
+// println!("* Welcome to {} *", self.get_tool_name());
+// println!("***************{}", "*".repeat(self.get_tool_name().len()));
+// }
+
+// let sep: String = path::MAIN_SEPARATOR.to_string();
+
+// let mut progress: usize;
+// let mut old_progress: usize = 1;
+
+// if !input_file.contains(&sep) && !input_file.contains("/") {
+// input_file = format!("{}{}", working_directory, input_file);
+// }
+// if !output_file.contains(&sep) && !output_file.contains("/") {
+// output_file = format!("{}{}", working_directory, output_file);
+// }
+
+// if verbose {
+// println!("Reading data...")
+// };
+
+// let input = Raster::new(&input_file, "r")?;
+
+// let start = Instant::now();
+// let rows = input.configs.rows as isize;
+// let columns = input.configs.columns as isize;
+// let num_cells = rows * columns;
+// let nodata = input.configs.nodata;
+
+// let mut output = Raster::initialize_using_file(&output_file, &input);
+// let mut background_val = (i32::min_value() + 1) as f64;
+// output.reinitialize_values(background_val);
+
+// /*
+// Find the data edges. This is complicated by the fact that DEMs frequently
+// have nodata edges, whereby the DEM does not occupy the full extent of
+// the raster. One approach to doing this would be simply to scan the
+// raster, looking for cells that neighbour nodata values. However, this
+// assumes that there are no interior nodata holes in the dataset. Instead,
+// the approach used here is to perform a region-growing operation, looking
+// for nodata values along the raster's edges.
+// */
+
+// let mut queue: VecDeque<(isize, isize)> =
+// VecDeque::with_capacity((rows * columns) as usize);
+// for row in 0..rows {
+// /*
+// Note that this is only possible because Whitebox rasters
+// allow you to address cells beyond the raster extent but
+// return the nodata value for these regions.
+// */
+// queue.push_back((row, -1));
+// queue.push_back((row, columns));
+// }
+
+// for col in 0..columns {
+// queue.push_back((-1, col));
+// queue.push_back((rows, col));
+// }
+
+// /*
+// minheap is the priority queue. Note that I've tested using integer-based
+// priority values, by multiplying the elevations, but this didn't result
+// in a significant performance gain over the use of f64s.
+// */
+// let mut minheap = BinaryHeap::with_capacity((rows * columns) as usize);
+// let mut num_solved_cells = 0;
+// let mut zin_n: f64; // value of neighbour of row, col in input raster
+// let mut zout: f64; // value of row, col in output raster
+// let mut zout_n: f64; // value of neighbour of row, col in output raster
+// let dx = [1, 1, 1, 0, -1, -1, -1, 0];
+// let dy = [-1, 0, 1, 1, 1, 0, -1, -1];
+// let (mut row, mut col): (isize, isize);
+// let (mut row_n, mut col_n): (isize, isize);
+// while !queue.is_empty() {
+// let cell = queue.pop_front().unwrap();
+// row = cell.0;
+// col = cell.1;
+// for n in 0..8 {
+// row_n = row + dy[n];
+// col_n = col + dx[n];
+// zin_n = input[(row_n, col_n)];
+// zout_n = output[(row_n, col_n)];
+// if zout_n == background_val {
+// if zin_n == nodata {
+// output[(row_n, col_n)] = nodata;
+// queue.push_back((row_n, col_n));
+// } else {
+// output[(row_n, col_n)] = zin_n;
+// // Push it onto the priority queue for the priority flood operation
+// minheap.push(GridCell {
+// row: row_n,
+// column: col_n,
+// priority: zin_n,
+// });
+// }
+// num_solved_cells += 1;
+// }
+// }
+
+// if verbose {
+// progress = (100.0_f64 * num_solved_cells as f64 / (num_cells - 1) as f64) as usize;
+// if progress != old_progress {
+// println!("progress: {}%", progress);
+// old_progress = progress;
+// }
+// }
+// }
+
+// // Perform the priority flood operation.
+// while !minheap.is_empty() {
+// let cell = minheap.pop().unwrap();
+// row = cell.row;
+// col = cell.column;
+// zout = output[(row, col)];
+// for n in 0..8 {
+// row_n = row + dy[n];
+// col_n = col + dx[n];
+// zout_n = output[(row_n, col_n)];
+// if zout_n == background_val {
+// zin_n = input[(row_n, col_n)];
+// if zin_n != nodata {
+// if zin_n < zout {
+// zin_n = zout;
+// } // We're in a depression. Raise the elevation.
+// output[(row_n, col_n)] = zin_n;
+// minheap.push(GridCell {
+// row: row_n,
+// column: col_n,
+// priority: zin_n,
+// });
+// } else {
+// // Interior nodata cells are still treated as nodata and are not filled.
+// output[(row_n, col_n)] = nodata;
+// num_solved_cells += 1;
+// }
+// }
+// }
+
+// if verbose {
+// num_solved_cells += 1;
+// progress = (100.0_f64 * num_solved_cells as f64 / (num_cells - 1) as f64) as usize;
+// if progress != old_progress {
+// println!("Progress (Loop 1 of 2): {}%", progress);
+// old_progress = progress;
+// }
+// }
+// }
+
+// background_val = nodata;
+// if zero_background {
+// background_val = 0f64;
+// }
+// for row in 0..rows {
+// for col in 0..columns {
+// if output[(row, col)] > input[(row, col)] {
+// output[(row, col)] = output[(row, col)] - input[(row, col)];
+// } else {
+// if input[(row, col)] != nodata {
+// output[(row, col)] = background_val;
+// } else {
+// output[(row, col)] = nodata;
+// }
+// }
+// }
+// if verbose {
+// progress = (100.0_f64 * row as f64 / (rows - 1) as f64) as usize;
+// if progress != old_progress {
+// println!("Progress (Loop 2 of 2): {}%", progress);
+// old_progress = progress;
+// }
+// }
+// }
+
+// let elapsed_time = get_formatted_elapsed_time(start);
+// output.configs.data_type = DataType::F32;
+// output.configs.palette = "qual.plt".to_string();
+// output.configs.photometric_interp = PhotometricInterpretation::Categorical;
+// output.add_metadata_entry(format!(
+// "Created by whitebox_tools\' {} tool",
+// self.get_tool_name()
+// ));
+// output.add_metadata_entry(format!("Input file: {}", input_file));
+// output.add_metadata_entry(format!("Elapsed Time (excluding I/O): {}", elapsed_time));
+
+// if verbose {
+// println!("Saving data...")
+// };
+// let _ = match output.write() {
+// Ok(_) => {
+// if verbose {
+// println!("Output file written")
+// }
+// }
+// Err(e) => return Err(e),
+// };
+// if verbose {
+// println!(
+// "{}",
+// &format!("Elapsed Time (excluding I/O): {}", elapsed_time)
+// );
+// }
+
+// Ok(())
+// }
+// }
+
+// #[derive(PartialEq, Debug)]
+// struct GridCell {
+// row: isize,
+// column: isize,
+// priority: f64,
+// }
+
+// impl Eq for GridCell {}
+
+// impl PartialOrd for GridCell {
+// fn partial_cmp(&self, other: &Self) -> Option {
+// other.priority.partial_cmp(&self.priority)
+// }
+// }
+
+// impl Ord for GridCell {
+// fn cmp(&self, other: &GridCell) -> Ordering {
+// let ord = self.partial_cmp(other).unwrap();
+// match ord {
+// Ordering::Greater => Ordering::Less,
+// Ordering::Less => Ordering::Greater,
+// Ordering::Equal => ord,
+// }
+// }
+// }
diff --git a/src/tools/hydro_analysis/dinf_flow_accum.rs b/src/tools/hydro_analysis/dinf_flow_accum.rs
index 6352c1e43..1f498b58f 100644
--- a/src/tools/hydro_analysis/dinf_flow_accum.rs
+++ b/src/tools/hydro_analysis/dinf_flow_accum.rs
@@ -194,7 +194,7 @@ impl WhiteboxTool for DInfFlowAccumulation {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/hydro_analysis/dinf_mass_flux.rs b/src/tools/hydro_analysis/dinf_mass_flux.rs
index c46a80868..107d21a22 100644
--- a/src/tools/hydro_analysis/dinf_mass_flux.rs
+++ b/src/tools/hydro_analysis/dinf_mass_flux.rs
@@ -169,7 +169,7 @@ impl WhiteboxTool for DInfMassFlux {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/hydro_analysis/dinf_pointer.rs b/src/tools/hydro_analysis/dinf_pointer.rs
index b354cb3e3..24f9ca443 100644
--- a/src/tools/hydro_analysis/dinf_pointer.rs
+++ b/src/tools/hydro_analysis/dinf_pointer.rs
@@ -142,7 +142,7 @@ impl WhiteboxTool for DInfPointer {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/hydro_analysis/downslope_distance_to_stream.rs b/src/tools/hydro_analysis/downslope_distance_to_stream.rs
index 92b39c18e..53aa939f0 100644
--- a/src/tools/hydro_analysis/downslope_distance_to_stream.rs
+++ b/src/tools/hydro_analysis/downslope_distance_to_stream.rs
@@ -135,7 +135,7 @@ impl WhiteboxTool for DownslopeDistanceToStream {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/hydro_analysis/downslope_flowpath_length.rs b/src/tools/hydro_analysis/downslope_flowpath_length.rs
index d698fb510..297bd49d8 100644
--- a/src/tools/hydro_analysis/downslope_flowpath_length.rs
+++ b/src/tools/hydro_analysis/downslope_flowpath_length.rs
@@ -166,7 +166,7 @@ impl WhiteboxTool for DownslopeFlowpathLength {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/hydro_analysis/elevation_above_stream.rs b/src/tools/hydro_analysis/elevation_above_stream.rs
index 0ec2d03fa..1a3b1bc68 100644
--- a/src/tools/hydro_analysis/elevation_above_stream.rs
+++ b/src/tools/hydro_analysis/elevation_above_stream.rs
@@ -147,7 +147,7 @@ impl WhiteboxTool for ElevationAboveStream {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/hydro_analysis/elevation_above_stream_euclidean.rs b/src/tools/hydro_analysis/elevation_above_stream_euclidean.rs
index 919e33996..6960e9e20 100644
--- a/src/tools/hydro_analysis/elevation_above_stream_euclidean.rs
+++ b/src/tools/hydro_analysis/elevation_above_stream_euclidean.rs
@@ -142,7 +142,7 @@ impl WhiteboxTool for ElevationAboveStreamEuclidean {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/hydro_analysis/fd8_flow_accum.rs b/src/tools/hydro_analysis/fd8_flow_accum.rs
index f08de5d27..e54466d53 100644
--- a/src/tools/hydro_analysis/fd8_flow_accum.rs
+++ b/src/tools/hydro_analysis/fd8_flow_accum.rs
@@ -2,7 +2,7 @@
This tool is part of the WhiteboxTools geospatial analysis library.
Authors: Dr. John Lindsay
Created: 26/06/2017
-Last Modified: 18/10/2019
+Last Modified: 21/11/2019
License: MIT
*/
@@ -208,7 +208,7 @@ impl WhiteboxTool for FD8FlowAccumulation {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
@@ -220,55 +220,52 @@ impl WhiteboxTool for FD8FlowAccumulation {
if vec.len() > 1 {
keyval = true;
}
- if vec[0].to_lowercase() == "-i"
- || vec[0].to_lowercase() == "--input"
- || vec[0].to_lowercase() == "--dem"
- {
- if keyval {
- input_file = vec[1].to_string();
+ let flag_val = vec[0].to_lowercase().replace("--", "-");
+ if flag_val == "-i" || flag_val == "-input" || flag_val == "-dem" {
+ input_file = if keyval {
+ vec[1].to_string()
} else {
- input_file = args[i + 1].to_string();
- }
- } else if vec[0].to_lowercase() == "-o" || vec[0].to_lowercase() == "--output" {
- if keyval {
- output_file = vec[1].to_string();
+ args[i + 1].to_string()
+ };
+ } else if flag_val == "-o" || flag_val == "-output" {
+ output_file = if keyval {
+ vec[1].to_string()
} else {
- output_file = args[i + 1].to_string();
- }
- } else if vec[0].to_lowercase() == "-out_type" || vec[0].to_lowercase() == "--out_type"
- {
- if keyval {
- out_type = vec[1].to_lowercase();
+ args[i + 1].to_string()
+ };
+ } else if flag_val == "-out_type" {
+ out_type = if keyval {
+ vec[1].to_lowercase()
} else {
- out_type = args[i + 1].to_lowercase();
- }
- if out_type.contains("specific") || out_type.contains("sca") {
- out_type = String::from("sca");
+ args[i + 1].to_lowercase()
+ };
+ out_type = if out_type.contains("specific") || out_type.contains("sca") {
+ String::from("sca")
} else if out_type.contains("cells") {
- out_type = String::from("cells");
+ String::from("cells")
} else {
- out_type = String::from("ca");
- }
- } else if vec[0].to_lowercase() == "-exponent" || vec[0].to_lowercase() == "--exponent"
- {
- if keyval {
- exponent = vec[1].to_string().parse::().unwrap();
+ String::from("ca")
+ };
+ } else if flag_val == "-exponent" {
+ exponent = if keyval {
+ vec[1].to_string().parse::().unwrap()
} else {
- exponent = args[i + 1].to_string().parse::().unwrap();
- }
- } else if vec[0].to_lowercase() == "-threshold"
- || vec[0].to_lowercase() == "--threshold"
- {
- if keyval {
- convergence_threshold = vec[1].to_string().parse::().unwrap();
+ args[i + 1].to_string().parse::().unwrap()
+ };
+ } else if flag_val == "-threshold" {
+ convergence_threshold = if keyval {
+ vec[1].to_string().parse::().unwrap()
} else {
- convergence_threshold = args[i + 1].to_string().parse::().unwrap();
+ args[i + 1].to_string().parse::().unwrap()
+ };
+ if convergence_threshold == 0f64 {
+ convergence_threshold = f64::INFINITY;
}
- } else if vec[0].to_lowercase() == "-log" || vec[0].to_lowercase() == "--log" {
+ } else if flag_val == "-log" {
if vec.len() == 1 || !vec[1].to_string().to_lowercase().contains("false") {
log_transform = true;
}
- } else if vec[0].to_lowercase() == "-clip" || vec[0].to_lowercase() == "--clip" {
+ } else if flag_val == "-clip" {
if vec.len() == 1 || !vec[1].to_string().to_lowercase().contains("false") {
clip_max = true;
}
@@ -389,7 +386,7 @@ impl WhiteboxTool for FD8FlowAccumulation {
];
let (mut max_slope, mut slope): (f64, f64);
let mut dir: i8;
-
+ let mut total_weights: f64;
while !stack.is_empty() {
let cell = stack.pop().unwrap();
row = cell.0;
@@ -398,7 +395,7 @@ impl WhiteboxTool for FD8FlowAccumulation {
fa = output[(row, col)];
num_inflowing[(row, col)] = -1i8;
- let mut total_weights = 0.0;
+ total_weights = 0.0;
let mut weights: [f64; 8] = [0.0; 8];
let mut downslope: [bool; 8] = [false; 8];
if fa < convergence_threshold {
diff --git a/src/tools/hydro_analysis/fd8_pointer.rs b/src/tools/hydro_analysis/fd8_pointer.rs
index da64c8a2e..78da9f8d0 100644
--- a/src/tools/hydro_analysis/fd8_pointer.rs
+++ b/src/tools/hydro_analysis/fd8_pointer.rs
@@ -146,7 +146,7 @@ impl WhiteboxTool for FD8Pointer {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/hydro_analysis/fill_burn.rs b/src/tools/hydro_analysis/fill_burn.rs
index e4e14bb08..ad9eaaaed 100644
--- a/src/tools/hydro_analysis/fill_burn.rs
+++ b/src/tools/hydro_analysis/fill_burn.rs
@@ -149,7 +149,7 @@ impl WhiteboxTool for FillBurn {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
@@ -534,9 +534,8 @@ impl WhiteboxTool for FillBurn {
// Perform the priority flood operation.
- let min_val = dem.configs.minimum;
- let elev_digits = ((dem.configs.maximum - min_val) as i64).to_string().len();
- let elev_multiplier = 10.0_f64.powi((7 - elev_digits) as i32);
+ let elev_digits = (dem.configs.maximum as i64).to_string().len();
+ let elev_multiplier = 10.0_f64.powi((12 - elev_digits) as i32);
let small_num = 1.0 / elev_multiplier as f64;
while !minheap.is_empty() {
diff --git a/src/tools/hydro_analysis/fill_depressions.rs b/src/tools/hydro_analysis/fill_depressions.rs
index 8f66f68ed..10c9e8620 100644
--- a/src/tools/hydro_analysis/fill_depressions.rs
+++ b/src/tools/hydro_analysis/fill_depressions.rs
@@ -2,20 +2,24 @@
This tool is part of the WhiteboxTools geospatial analysis library.
Authors: Dr. John Lindsay
Created: 28/06/2017
-Last Modified: 18/10/2019
+Last Modified: 24/11/2019
License: MIT
*/
use crate::raster::*;
use crate::tools::*;
+use crate::structures::Array2D;
use std::cmp::Ordering;
-use std::collections::BinaryHeap;
-use std::collections::VecDeque;
+use std::cmp::Ordering::Equal;
+use std::collections::{BinaryHeap, VecDeque};
use std::env;
use std::f64;
use std::i32;
use std::io::{Error, ErrorKind};
use std::path;
+use std::sync::mpsc;
+use std::sync::Arc;
+use std::thread;
/// This tool can be used to fill all of the depressions in a digital elevation model (DEM) and to remove the f
/// lat areas. This is a common pre-processing step required by many flow-path analysis tools to ensure continuous
@@ -24,7 +28,9 @@ use std::path;
/// edge cells, and visiting cells from lowest order using a priority queue. As such, it is based on the algorithm
/// first proposed by Wang and Liu (2006). It is currently the most efficient depression-removal algorithm available
/// in WhiteboxTools, although it is not significantly more efficient than the `BreachDepressions` tool, which is
-/// known to provide a solution to depression removal with less impact of the DEM.
+/// known to provide a solution to depression removal with less impact of the DEM. Furthermore, the `BreachDepressionsLeastCost`,
+/// while less efficient than either other hydrological preprocessing methods, often provides a lower impact solution
+/// to topographic depression.
///
/// If the input DEM has gaps, or missing-data holes, that contain NoData values, it is better to use the
/// `FillMissingData` tool to repair these gaps. This tool will interpolate values across the gaps and produce
@@ -33,12 +39,18 @@ use std::path;
/// of valid data. Any NoData areas along the edge of the grid will simply be ignored and will remain NoData areas in
/// the output image.
///
+/// In comparison with the `BreachDepressionsLeastCost` tool, the depression filling method often provides a less
+/// satisfactory, higher impact, depression removal solution and is often less efficient. **It is advisable that users
+/// try the `BreachDepressionsLeastCost` tool to remove depressions from their DEMs first**. The `BreachDepressionsLeastCost`
+/// tool is particularly well suited to breaching through road embankments in fine-resolution DEMs. Nonetheless, there are
+/// applications for which full depression filling using the `FillDepressions` tool may be preferred.
+///
/// # Reference
/// Wang, L. and Lui, H. 2006. An efficient method for identifying and filling surface depressions in digital elevation
/// models for hydrologic analysis and modelling. International Journal of Geographical Information Science, 20(2): 193-213.
///
/// # See Also
-/// `BreachDepressions`, `FillMissingData`
+/// `BreachDepressionsLeastCost`, `BreachDepressions`, `FillMissingData`
pub struct FillDepressions {
name: String,
description: String,
@@ -93,6 +105,15 @@ impl FillDepressions {
optional: true,
});
+ parameters.push(ToolParameter {
+ name: "Maximum depth (z units)".to_owned(),
+ flags: vec!["--max_depth".to_owned()],
+ description: "Optional maximum depression depth to fill.".to_owned(),
+ parameter_type: ParameterType::Float,
+ default_value: None,
+ optional: true,
+ });
+
let sep: String = path::MAIN_SEPARATOR.to_string();
let p = format!("{}", env::current_dir().unwrap().display());
let e = format!("{}", env::current_exe().unwrap().display());
@@ -158,11 +179,12 @@ impl WhiteboxTool for FillDepressions {
let mut output_file = String::new();
let mut fix_flats = false;
let mut flat_increment = f64::NAN;
+ let mut max_depth = f64::INFINITY;
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
@@ -197,6 +219,12 @@ impl WhiteboxTool for FillDepressions {
} else {
args[i + 1].to_string().parse::().unwrap()
};
+ } else if flag_val == "-max_depth" {
+ max_depth = if keyval {
+ vec[1].to_string().parse::().unwrap()
+ } else {
+ args[i + 1].to_string().parse::().unwrap()
+ };
}
}
@@ -227,372 +255,425 @@ impl WhiteboxTool for FillDepressions {
let start = Instant::now();
let rows = input.configs.rows as isize;
let columns = input.configs.columns as isize;
- let num_cells = rows * columns;
let nodata = input.configs.nodata;
-
- // let min_val = input.configs.minimum;
- // let elev_digits = ((input.configs.maximum - min_val) as i64).to_string().len();
- // let elev_multiplier = 10.0_f64.powi((7 - elev_digits) as i32);
- // let mut small_num = 0.0;
- // if fix_flats {
- // small_num = 1.0 / elev_multiplier as f64;
- // }
+ let resx = input.configs.resolution_x;
+ let resy = input.configs.resolution_y;
+ let diagres = (resx * resx + resy * resy).sqrt();
let small_num = if fix_flats && !flat_increment.is_nan() {
flat_increment
} else if fix_flats {
- let min_val = input.configs.minimum;
- let elev_digits = ((input.configs.maximum - min_val) as i64).to_string().len();
- let elev_multiplier = 10.0_f64.powi((6 - elev_digits) as i32);
- 1.0_f64 / elev_multiplier as f64
+ let elev_digits = (input.configs.maximum as i64).to_string().len();
+ let elev_multiplier = 10.0_f64.powi((15 - elev_digits) as i32);
+ 1.0_f64 / elev_multiplier as f64 * diagres.ceil()
} else {
0f64
};
let mut output = Raster::initialize_using_file(&output_file, &input);
+ output.set_data_from_raster(&input)?;
output.configs.data_type = DataType::F64;
- let background_val = (i32::min_value() + 1) as f64;
- output.reinitialize_values(background_val);
-
- /*
- Find the data edges. This is complicated by the fact that DEMs frequently
- have nodata edges, whereby the DEM does not occupy the full extent of
- the raster. One approach to doing this would be simply to scan the
- raster, looking for cells that neighbour nodata values. However, this
- assumes that there are no interior nodata holes in the dataset. Instead,
- the approach used here is to perform a region-growing operation, looking
- for nodata values along the raster's edges.
- */
-
- let mut queue: VecDeque<(isize, isize)> =
- VecDeque::with_capacity((rows * columns) as usize);
- for row in 0..rows {
- /*
- Note that this is only possible because Whitebox rasters
- allow you to address cells beyond the raster extent but
- return the nodata value for these regions.
- */
- queue.push_back((row, -1));
- queue.push_back((row, columns));
- }
+ output.configs.display_min = input.configs.display_min;
+ output.configs.display_max = input.configs.display_max;
- for col in 0..columns {
- queue.push_back((-1, col));
- queue.push_back((rows, col));
- }
+ // drop(input); // input is no longer needed.
- /*
- minheap is the priority queue. Note that I've tested using integer-based
- priority values, by multiplying the elevations, but this didn't result
- in a significant performance gain over the use of f64s.
- */
- let mut minheap = BinaryHeap::with_capacity((rows * columns) as usize);
- let mut num_solved_cells = 0;
- let mut zin_n: f64; // value of neighbour of row, col in input raster
- let mut zout: f64; // value of row, col in output raster
- let mut zout_n: f64; // value of neighbour of row, col in output raster
+
+ let (mut col, mut row): (isize, isize);
+ let (mut rn, mut cn): (isize, isize);
+ let (mut z, mut zn): (f64, f64);
let dx = [1, 1, 1, 0, -1, -1, -1, 0];
let dy = [-1, 0, 1, 1, 1, 0, -1, -1];
- let (mut row, mut col): (isize, isize);
- let (mut row_n, mut col_n): (isize, isize);
- while !queue.is_empty() {
- let cell = queue.pop_front().unwrap();
- row = cell.0;
- col = cell.1;
- for n in 0..8 {
- row_n = row + dy[n];
- col_n = col + dx[n];
- zin_n = input.get_value(row_n, col_n);
- zout_n = output[(row_n, col_n)];
- if zout_n == background_val {
- if zin_n == nodata {
- output.set_value(row_n, col_n, nodata);
- queue.push_back((row_n, col_n));
- } else {
- output[(row_n, col_n)] = zin_n;
- // Push it onto the priority queue for the priority flood operation
- minheap.push(GridCell {
- row: row_n,
- column: col_n,
- priority: zin_n,
- });
+
+ // Find pit cells. This step is parallelized.
+ let num_procs = num_cpus::get() as isize;
+ let output2 = Arc::new(output);
+ let (tx, rx) = mpsc::channel();
+ for tid in 0..num_procs {
+ let output2 = output2.clone();
+ let tx = tx.clone();
+ thread::spawn(move || {
+ let mut z: f64;
+ let mut zn: f64;
+ let mut flag: bool;
+ let mut pits = vec![];
+ for row in (1..rows-1).filter(|r| r % num_procs == tid) {
+ for col in 1..columns-1 {
+ z = output2.get_value(row, col);
+ if z != nodata {
+ flag = true;
+ for n in 0..8 {
+ zn = output2.get_value(row + dy[n], col + dx[n]);
+ if zn < z || zn == nodata { // It either has a lower neighbour or is an edge cell.
+ flag = false;
+ break;
+ }
+ }
+ if flag { // it's a cell with undefined flow
+ pits.push((row, col, z));
+ }
+ }
}
- num_solved_cells += 1;
}
- }
+ tx.send(pits).unwrap();
+ });
+ }
+ let mut undefined_flow_cells = vec![];
+ for p in 0..num_procs {
+ let mut pits = rx.recv().unwrap();
+ undefined_flow_cells.append(&mut pits);
+
if verbose {
- progress = (100.0_f64 * num_solved_cells as f64 / (num_cells - 1) as f64) as usize;
+ progress = (100.0_f64 * (p + 1) as f64 / num_procs as f64) as usize;
if progress != old_progress {
- println!("progress: {}%", progress);
+ println!("Finding pit cells: {}%", progress);
old_progress = progress;
}
}
}
- /*
- The following code follows the scenario of a priority-flood method without the extra
- complication of an embedded region-growing operation for in-depression sites.
- */
-
- // Perform the priority flood operation.
- while !minheap.is_empty() {
- let cell = minheap.pop().unwrap();
- row = cell.row;
- col = cell.column;
- zout = output[(row, col)];
- for n in 0..8 {
- row_n = row + dy[n];
- col_n = col + dx[n];
- zout_n = output[(row_n, col_n)];
- if zout_n == background_val {
- zin_n = input[(row_n, col_n)];
- if zin_n != nodata {
- if zin_n < (zout + small_num) {
- zin_n = zout + small_num;
- } // We're in a depression. Raise the elevation.
- output[(row_n, col_n)] = zin_n;
- minheap.push(GridCell {
- row: row_n,
- column: col_n,
- priority: zin_n,
- });
+ output = match Arc::try_unwrap(output2) {
+ Ok(val) => val,
+ Err(_) => panic!("Error unwrapping 'output'"),
+ };
+
+ let num_deps = undefined_flow_cells.len();
+
+
+ // Now we need to perform an in-place depression filling
+ let mut minheap = BinaryHeap::new();
+ let mut visited: Array2D = Array2D::new(rows, columns, 0, -1)?;
+ let mut flats: Array2D = Array2D::new(rows, columns, 0, -1)?;
+ let mut possible_outlets = vec![];
+ // solve from highest to lowest
+ undefined_flow_cells.sort_by(|a, b| a.2.partial_cmp(&b.2).unwrap_or(Equal));
+ let mut pit_id = 1;
+ let mut flag: bool;
+ let mut z_pit: f64;
+ while let Some(cell) = undefined_flow_cells.pop() {
+ row = cell.0;
+ col = cell.1;
+ if flats.get_value(row, col) != 1 { // if it's already in a solved site, don't do it a second time.
+ // First there is a priority region-growing operation to find the outlets.
+ z_pit = output.get_value(row, col);
+ minheap.clear();
+ minheap.push(GridCell {
+ row: row,
+ column: col,
+ priority: z_pit,
+ });
+ visited.set_value(row, col, 1);
+ let mut outlet_found = false;
+ let mut outlet_z = f64::INFINITY;
+ let mut queue = VecDeque::new();
+ while let Some(cell2) = minheap.pop() {
+ z = cell2.priority;
+ if outlet_found && z > outlet_z {
+ break;
+ }
+ if z - z_pit > max_depth {
+ // no outlet could be found that was low enough.
+ break;
+ }
+ if !outlet_found {
+ for n in 0..8 {
+ cn = cell2.column + dx[n];
+ rn = cell2.row + dy[n];
+ if visited.get_value(rn, cn) == 0 {
+ zn = output.get_value(rn, cn);
+ if !outlet_found {
+ if zn >= z && zn != nodata {
+ minheap.push(GridCell {
+ row: rn,
+ column: cn,
+ priority: zn,
+ });
+ visited.set_value(rn, cn, 1);
+ } else if zn != nodata { // zn < z
+ // 'cell' has a lower neighbour that hasn't already passed through minheap.
+ // Therefore, 'cell' is a pour point cell.
+ outlet_found = true;
+ outlet_z = z;
+ queue.push_back((cell2.row, cell2.column));
+ possible_outlets.push((cell2.row, cell2.column));
+ }
+ } else if zn == outlet_z { // We've found the outlet but are still looking for additional outlets.
+ minheap.push(GridCell {
+ row: rn,
+ column: cn,
+ priority: zn,
+ });
+ visited.set_value(rn, cn, 1);
+ }
+ }
+ }
} else {
- // Interior nodata cells are still treated as nodata and are not filled.
- output[(row_n, col_n)] = nodata;
- num_solved_cells += 1;
+ if z == outlet_z {
+ flag = false;
+ for n in 0..8 {
+ cn = cell2.column + dx[n];
+ rn = cell2.row + dy[n];
+ if visited.get_value(rn, cn) == 0 {
+ zn = output.get_value(rn, cn);
+ if zn < z {
+ flag = true;
+ } else if zn == outlet_z {
+ minheap.push(GridCell {
+ row: rn,
+ column: cn,
+ priority: zn,
+ });
+ visited.set_value(rn, cn, 1);
+ }
+ }
+ }
+ if flag { // it's an outlet
+ queue.push_back((cell2.row, cell2.column));
+ possible_outlets.push((cell2.row, cell2.column));
+ } else {
+ visited.set_value(cell2.row, cell2.column, 1);
+ }
+ }
+ }
+ }
+
+ // Now that we have the outlets, raise the interior of the depression
+ if outlet_found {
+ while let Some(cell2) = queue.pop_front() {
+ for n in 0..8 {
+ rn = cell2.0 + dy[n];
+ cn = cell2.1 + dx[n];
+ if visited.get_value(rn, cn) == 1 {
+ visited.set_value(rn, cn, 0);
+ queue.push_back((rn, cn));
+ z = output.get_value(rn, cn);
+ if z < outlet_z {
+ output.set_value(rn, cn, outlet_z);
+ flats.set_value(rn, cn, 1);
+ } else if z == outlet_z {
+ flats.set_value(rn, cn, 1);
+ }
+ }
+ }
+ }
+ } else {
+ queue.push_back((row, col)); // start at the pit cell and clean up visited
+ while let Some(cell2) = queue.pop_front() {
+ for n in 0..8 {
+ rn = cell2.0 + dy[n];
+ cn = cell2.1 + dx[n];
+ if visited.get_value(rn, cn) == 1 {
+ visited.set_value(rn, cn, 0);
+ queue.push_back((rn, cn));
+ }
+ }
}
}
}
if verbose {
- num_solved_cells += 1;
- progress = (100.0_f64 * num_solved_cells as f64 / (num_cells - 1) as f64) as usize;
+ progress = (100.0_f64 * pit_id as f64 / num_deps as f64) as usize;
if progress != old_progress {
- println!("Progress: {}%", progress);
+ println!("Filling depressions: {}%", progress);
old_progress = progress;
}
}
+ pit_id += 1;
+ }
+
+ drop(visited);
+
+ if small_num > 0f64 && fix_flats { // fix the flats
+ if verbose {
+ println!("Fixing flow on flats...");
+ println!("Flats increment value: {}", small_num);
+ }
+ // Some of the potential outlets really will have lower cells.
+ // let mut queue = VecDeque::new();
+ minheap.clear();
+ while let Some(cell) = possible_outlets.pop() {
+ z = output.get_value(cell.0, cell.1);
+ flag = false;
+ for n in 0..8 {
+ rn = cell.0 + dy[n];
+ cn = cell.1 + dx[n];
+ zn = output.get_value(rn, cn);
+ if zn < z && zn != nodata {
+ flag = true;
+ break;
+ }
+ }
+ if flag { // it's confirmed as an outlet
+ minheap.push(GridCell {
+ row: cell.0,
+ column: cell.1,
+ priority: z,
+ });
+ }
+ }
+
+ let num_outlets = minheap.len();
+
+ while let Some(cell) = minheap.pop() {
+ if flats.get_value(cell.row, cell.column) != 3 {
+ z = output.get_value(cell.row, cell.column);
+ flats.set_value(cell.row, cell.column, 3);
+ let mut outlets = vec![];
+ outlets.push(cell);
+ // Are there any other outlet cells at the same elevation (likely for the same feature)
+ flag = true;
+ while flag {
+ match minheap.peek() {
+ Some(cell2) => {
+ if cell2.priority == z {
+ flats.set_value(cell2.row, cell2.column, 3);
+ outlets.push(minheap.pop().unwrap());
+ } else {
+ flag = false;
+ }
+ },
+ None => {
+ flag = false;
+ }
+ }
+
+ }
+ // let mut queue = VecDeque::new();
+ let mut minheap2 = BinaryHeap::new();
+ for cell2 in &outlets {
+ z = output.get_value(cell2.row, cell2.column);
+ for n in 0..8 {
+ rn = cell2.row + dy[n];
+ cn = cell2.column + dx[n];
+ if flats.get_value(rn, cn) != 3 {
+ zn = output.get_value(rn, cn);
+ if zn == z && zn != nodata {
+ // queue.push_back((rn, cn, z));
+ minheap2.push(GridCell2 {
+ row: rn,
+ column: cn,
+ z: z,
+ priority: input.get_value(rn, cn),
+ });
+ output.set_value(rn, cn, z + small_num);
+ flats.set_value(rn, cn, 3);
+ }
+ }
+ }
+ }
+ // Now fix the flats
+ while let Some(cell2) = minheap2.pop() {
+ z = output.get_value(cell2.row, cell2.column);
+ for n in 0..8 {
+ rn = cell2.row + dy[n];
+ cn = cell2.column + dx[n];
+ if flats.get_value(rn, cn) != 3 {
+ zn = output.get_value(rn, cn);
+ if zn < z + small_num && zn >= cell2.z && zn != nodata {
+ // queue.push_back((rn, cn, cell2.2));
+ minheap2.push(GridCell2 {
+ row: rn,
+ column: cn,
+ z: cell2.z,
+ priority: input.get_value(rn, cn),
+ });
+ output.set_value(rn, cn, z + small_num);
+ flats.set_value(rn, cn, 3);
+ }
+ }
+ }
+ }
+ // while let Some(cell2) = queue.pop_front() {
+ // z = output.get_value(cell2.0, cell2.1);
+ // for n in 0..8 {
+ // rn = cell2.0 + dy[n];
+ // cn = cell2.1 + dx[n];
+ // if flats.get_value(rn, cn) != 3 {
+ // zn = output.get_value(rn, cn);
+ // if zn < z + small_num && zn >= cell2.2 && zn != nodata {
+ // queue.push_back((rn, cn, cell2.2));
+ // output.set_value(rn, cn, z + small_num);
+ // flats.set_value(rn, cn, 3);
+ // }
+ // }
+ // }
+ // }
+ }
+
+ if verbose {
+ progress = (100.0_f64 * (1f64 - minheap.len() as f64 / num_outlets as f64)) as usize;
+ if progress != old_progress {
+ println!("Fixing flats: {}%", progress);
+ old_progress = progress;
+ }
+ }
+ }
+
+
+ // let mut queue = VecDeque::new();
+ // minheap.clear();
+ // while let Some(cell) = possible_outlets.pop() {
+ // z = output.get_value(cell.0, cell.1);
+ // flag = false;
+ // for n in 0..8 {
+ // rn = cell.0 + dy[n];
+ // cn = cell.1 + dx[n];
+ // zn = output.get_value(rn, cn);
+ // if zn < z && zn != nodata {
+ // flag = true;
+ // break;
+ // }
+ // }
+ // if flag {
+ // queue.push_back((cell.0, cell.1, z));
+ // flats.set_value(cell.0, cell.1, 2);
+ // } else {
+ // flats.set_value(cell.0, cell.1, 1);
+ // }
+ // }
+
+ // let mut flats_value: i8;
+
+ // while let Some(cell) = queue.pop_front() {
+ // z = output.get_value(cell.0, cell.1);
+ // flats_value = flats.get_value(cell.0, cell.1);
+ // if flats_value == 2 { // outlet cell
+ // for n in 0..8 {
+ // rn = cell.0 + dy[n];
+ // cn = cell.1 + dx[n];
+ // if flats.get_value(rn, cn) == 1 {
+ // zn = output.get_value(rn, cn);
+ // if zn == z {
+ // queue.push_back((rn, cn, z));
+ // output.set_value(rn, cn, z + small_num);
+ // flats.set_value(rn, cn, 3);
+ // }
+ // }
+ // }
+ // flats.set_value(cell.0, cell.1, 3);
+ // } else { // non-outlet cell
+ // for n in 0..8 {
+ // rn = cell.0 + dy[n];
+ // cn = cell.1 + dx[n];
+ // flats_value = flats.get_value(rn, cn);
+ // if flats_value == 0 || flats_value == 1 {
+ // zn = output.get_value(rn, cn);
+ // if zn < z + small_num && zn >= cell.2 && zn != nodata {
+ // queue.push_back((rn, cn, cell.2));
+ // output.set_value(rn, cn, z + small_num);
+ // flats.set_value(rn, cn, 3);
+ // } else if flats_value == 1 {
+ // flats.set_value(rn, cn, 3);
+ // }
+ // }
+ // }
+ // }
+ // }
}
- /*
- This code uses a slightly more complex priority flood approach that uses an embedded
- region-growing operation for cells within depressions. It offers a slight speed up
- over the traditional approach, but I have noticed that sometimes it doesn't work
- as expected. I'm not sure why and it would require some effort to track down the bug.
- Given that most DEMs have relatively few cells within depressions, the speed up of
- this approach is perhaps not worthwhile and it is certainly more complex.
- */
- // // Perform the priority flood operation.
- // while !minheap.is_empty() {
- // let cell = minheap.pop().unwrap();
- // row = cell.row;
- // col = cell.column;
- // zout = output[(row, col)];
- // for n in 0..8 {
- // row_n = row + dy[n];
- // col_n = col + dx[n];
- // zout_n = output[(row_n, col_n)];
- // if zout_n == background_val {
- // zin_n = input[(row_n, col_n)];
- // if zin_n != nodata {
- // if zin_n < (zout + small_num) {
- // // We're in a depression. Raise the elevation.
- // zout_n = zout + small_num;
- // output[(row_n, col_n)] = zout_n;
- // /*
- // Cells that are in the depression don't need to be discovered by
- // the more expensive priority-flood operation. Instead, perform
- // an efficient region-growing operation to find cells connected
- // to this cell that have elevations in the input DEM that are
- // less than the adjusted zout_n.
- // */
- // queue.push_back((row_n, col_n));
- // while !queue.is_empty() {
- // let cell = queue.pop_front().unwrap();
- // row = cell.0;
- // col = cell.1;
- // zout = output[(row, col)];
- // for n2 in 0..8 {
- // row_n = row + dy[n2];
- // col_n = col + dx[n2];
- // zout_n = output[(row_n, col_n)];
- // if zout_n == background_val {
- // zin_n = input[(row_n, col_n)];
- // if zin_n != nodata {
- // if zin_n < (zout + small_num) {
- // zout_n = zout + small_num;
- // output[(row_n, col_n)] = zout_n;
- // queue.push_back((row_n, col_n));
- // } else {
- // minheap.push(GridCell{ row: row_n, column: col_n, priority: zin_n });
- // output[(row_n, col_n)] = zin_n;
- // }
- // } else {
- // // Interior nodata cells are still treated as nodata and are not filled.
- // output[(row_n, col_n)] = nodata;
- // num_solved_cells += 1;
- // }
- // }
- // }
- // if verbose {
- // num_solved_cells += 1;
- // progress = (100.0_f64 * num_solved_cells as f64 / (num_cells - 1) as f64) as usize;
- // if progress != old_progress {
- // println!("Progress: {}%", progress);
- // old_progress = progress;
- // }
- // }
- // }
- // } else {
- // minheap.push(GridCell{ row: row_n, column: col_n, priority: zin_n });
- // output[(row_n, col_n)] = zin_n;
- // }
- // } else {
- // // Interior nodata cells are still treated as nodata and are not filled.
- // output[(row_n, col_n)] = nodata;
- // num_solved_cells += 1;
- // }
- // }
- // }
-
- // if verbose {
- // num_solved_cells += 1;
- // progress = (100.0_f64 * num_solved_cells as f64 / (num_cells - 1) as f64) as usize;
- // if progress != old_progress {
- // println!("Progress: {}%", progress);
- // old_progress = progress;
- // }
- // }
- // }
-
- /* This was an experiement with an approach that reduced the reliance on the priority queue.
- It works well but is slightly less efficient that the traditional approach. */
-
- // let mut output = Raster::initialize_using_file(&output_file, &input);
- // let background_val = (i32::max_value() - 1) as f64;
- // output.reinitialize_values(background_val);
-
- // /*
- // Find the data edges. This is complicated by the fact that DEMs frequently
- // have nodata edges, whereby the DEM does not occupy the full extent of
- // the raster. One approach to doing this would be simply to scan the
- // raster, looking for cells that neighbour nodata values. However, this
- // assumes that there are no interior nodata holes in the dataset. Instead,
- // the approach used here is to perform a region-growing operation, looking
- // for nodata values along the raster's edges.
- // */
- // //let mut stack = Vec::with_capacity((rows * columns) as usize);
- // let mut queue: VecDeque<(isize, isize)> = VecDeque::with_capacity((rows * columns) as usize);
- // for row in 0..rows {
- // /*
- // Note that this is only possible because Whitebox rasters
- // allow you to address cells beyond the raster extent but
- // return the nodata value for these regions.
- // */
- // queue.push_back((row, -1));
- // queue.push_back((row, columns));
- // }
-
- // for col in 0..columns {
- // queue.push_back((-1, col));
- // queue.push_back((rows, col));
- // }
-
- // /*
- // minheap is the priority queue. Note that I've tested using integer-based
- // priority values, by multiplying the elevations, but this didn't result
- // in a significant performance gain over the use of f64s.
- // */
- // let mut minheap = BinaryHeap::with_capacity((rows * columns) as usize);
- // let mut num_solved_cells = 0;
- // let mut zin_n: f64; // value of neighbour of row, col in input raster
- // let mut zout: f64; // value of row, col in output raster
- // let mut zout_n: f64; // value of neighbour of row, col in output raster
- // let dx = [ 1, 1, 1, 0, -1, -1, -1, 0 ];
- // let dy = [ -1, 0, 1, 1, 1, 0, -1, -1 ];
- // let (mut row, mut col): (isize, isize);
- // let (mut row_n, mut col_n): (isize, isize);
- // while !queue.is_empty() {
- // let cell = queue.pop_front().unwrap();
- // row = cell.0;
- // col = cell.1;
- // for n in 0..8 {
- // row_n = row + dy[n];
- // col_n = col + dx[n];
- // zin_n = input[(row_n, col_n)];
- // zout_n = output[(row_n, col_n)];
- // if zout_n == background_val {
- // if zin_n == nodata {
- // output[(row_n, col_n)] = nodata;
- // queue.push_back((row_n, col_n));
- // } else {
- // output[(row_n, col_n)] = zin_n;
- // // Push it onto the priority queue for the priority flood operation
- // minheap.push(GridCell{ row: row_n, column: col_n, priority: zin_n });
- // }
- // num_solved_cells += 1;
- // }
- // }
-
- // if verbose {
- // progress = (100.0_f64 * num_solved_cells as f64 / (num_cells - 1) as f64) as usize;
- // if progress != old_progress {
- // println!("progress: {}%", progress);
- // old_progress = progress;
- // }
- // }
- // }
-
- // // Perform the priority flood operation.
- // // let initial_heap_size = minheap.len();
- // // num_solved_cells = 0;
- // while !minheap.is_empty() {
- // let cell = minheap.pop().unwrap();
- // queue.push_back((cell.row, cell.column));
- // while !queue.is_empty() {
- // let cell = queue.pop_front().unwrap();
- // row = cell.0;
- // col = cell.1;
- // zout = output[(row, col)];
- // for n in 0..8 {
- // row_n = row + dy[n];
- // col_n = col + dx[n];
- // zout_n = output[(row_n, col_n)];
- // zin_n = input[(row_n, col_n)];
- // if zout_n > zin_n {
- // if zin_n != nodata {
- // if zin_n > zout + small_num {
- // output[(row_n, col_n)] = zin_n;
- // queue.push_back((row_n, col_n));
- // num_solved_cells += 1;
- // } else if zout + small_num < zout_n {
- // output[(row_n, col_n)] = zout + small_num;
- // queue.push_back((row_n, col_n));
- // }
- // } else {
- // output[(row_n, col_n)] = nodata;
- // queue.push_back((row_n, col_n));
- // num_solved_cells += 1;
- // }
- // }
- // }
-
- // if verbose {
- // progress = (100.0_f64 * num_solved_cells as f64 / (num_cells - 1) as f64) as usize;
- // if progress != old_progress {
- // println!("Progress: {}%", progress);
- // old_progress = progress;
- // }
- // }
- // }
- // }
-
- // if verbose { println!("Progress: 100%"); }
let elapsed_time = get_formatted_elapsed_time(start);
- output.configs.display_min = input.configs.display_min;
- output.configs.display_max = input.configs.display_max;
output.add_metadata_entry(format!(
"Created by whitebox_tools\' {} tool",
self.get_tool_name()
@@ -630,27 +711,41 @@ impl WhiteboxTool for FillDepressions {
struct GridCell {
row: isize,
column: isize,
- // priority: usize,
priority: f64,
}
impl Eq for GridCell {}
impl PartialOrd for GridCell {
- fn partial_cmp(&self, other: &GridCell) -> Option {
- // Some(other.priority.cmp(&self.priority))
+ fn partial_cmp(&self, other: &Self) -> Option {
other.priority.partial_cmp(&self.priority)
}
}
impl Ord for GridCell {
- fn cmp(&self, other: &GridCell) -> Ordering {
- // other.priority.cmp(&self.priority)
- let ord = self.partial_cmp(other).unwrap();
- match ord {
- Ordering::Greater => Ordering::Less,
- Ordering::Less => Ordering::Greater,
- Ordering::Equal => ord,
- }
+ fn cmp(&self, other: &Self) -> Ordering {
+ self.partial_cmp(other).unwrap()
+ }
+}
+
+#[derive(PartialEq, Debug)]
+struct GridCell2 {
+ row: isize,
+ column: isize,
+ z: f64,
+ priority: f64,
+}
+
+impl Eq for GridCell2 {}
+
+impl PartialOrd for GridCell2 {
+ fn partial_cmp(&self, other: &Self) -> Option {
+ other.priority.partial_cmp(&self.priority)
+ }
+}
+
+impl Ord for GridCell2 {
+ fn cmp(&self, other: &Self) -> Ordering {
+ self.partial_cmp(other).unwrap()
}
}
diff --git a/src/tools/hydro_analysis/fill_depressions_wang_and_lui.rs b/src/tools/hydro_analysis/fill_depressions_wang_and_lui.rs
new file mode 100644
index 000000000..a078a6f3f
--- /dev/null
+++ b/src/tools/hydro_analysis/fill_depressions_wang_and_lui.rs
@@ -0,0 +1,653 @@
+/*
+This tool is part of the WhiteboxTools geospatial analysis library.
+Authors: Dr. John Lindsay
+Created: 28/06/2017
+Last Modified: 05/12/2019
+License: MIT
+
+NOTE: This tool was originally named FillDepressions. However, I have updated the algorithm used by the
+FillDepressions tool to something that is often more efficient than the Wang and Lui method. As such,
+I have created this tool to house the original Wang and Lui based depression filling method for
+legacy reasons.
+*/
+
+use crate::raster::*;
+use crate::tools::*;
+use std::cmp::Ordering;
+use std::collections::BinaryHeap;
+use std::collections::VecDeque;
+use std::env;
+use std::f64;
+use std::i32;
+use std::io::{Error, ErrorKind};
+use std::path;
+
+/// This tool can be used to fill all of the depressions in a digital elevation model (DEM) and to remove the f
+/// lat areas. This is a common pre-processing step required by many flow-path analysis tools to ensure continuous
+/// flow from each grid cell to an outlet located along the grid edge. The `FillDepressions` algorithm is based on
+/// the computationally efficient approach of examining each cell based on its spill elevation, starting from the
+/// edge cells, and visiting cells from lowest order using a priority queue. As such, it is based on the algorithm
+/// first proposed by Wang and Liu (2006). It is currently the most efficient depression-removal algorithm available
+/// in WhiteboxTools, although it is not significantly more efficient than the `BreachDepressions` tool, which is
+/// known to provide a solution to depression removal with less impact of the DEM.
+///
+/// If the input DEM has gaps, or missing-data holes, that contain NoData values, it is better to use the
+/// `FillMissingData` tool to repair these gaps. This tool will interpolate values across the gaps and produce
+/// a more natural-looking surface than the flat areas that are produced by depression filling. Importantly, the
+/// `FillDepressions` tool algorithm implementation assumes that there are no 'donut hole' NoData gaps within the area
+/// of valid data. Any NoData areas along the edge of the grid will simply be ignored and will remain NoData areas in
+/// the output image.
+///
+/// # Reference
+/// Wang, L. and Lui, H. 2006. An efficient method for identifying and filling surface depressions in digital elevation
+/// models for hydrologic analysis and modelling. International Journal of Geographical Information Science, 20(2): 193-213.
+///
+/// # See Also
+/// `BreachDepressions`, `FillMissingData`
+pub struct FillDepressionsWangAndLui {
+ name: String,
+ description: String,
+ toolbox: String,
+ parameters: Vec,
+ example_usage: String,
+}
+
+impl FillDepressionsWangAndLui {
+ pub fn new() -> FillDepressionsWangAndLui {
+ // public constructor
+ let name = "FillDepressionsWangAndLui".to_string();
+ let toolbox = "Hydrological Analysis".to_string();
+ let description = "Fills all of the depressions in a DEM. Depression breaching should be preferred in most cases.".to_string();
+
+ let mut parameters = vec![];
+ parameters.push(ToolParameter {
+ name: "Input DEM File".to_owned(),
+ flags: vec!["-i".to_owned(), "--dem".to_owned()],
+ description: "Input raster DEM file.".to_owned(),
+ parameter_type: ParameterType::ExistingFile(ParameterFileType::Raster),
+ default_value: None,
+ optional: false,
+ });
+
+ parameters.push(ToolParameter {
+ name: "Output File".to_owned(),
+ flags: vec!["-o".to_owned(), "--output".to_owned()],
+ description: "Output raster file.".to_owned(),
+ parameter_type: ParameterType::NewFile(ParameterFileType::Raster),
+ default_value: None,
+ optional: false,
+ });
+
+ parameters.push(ToolParameter {
+ name: "Fix flat areas?".to_owned(),
+ flags: vec!["--fix_flats".to_owned()],
+ description:
+ "Optional flag indicating whether flat areas should have a small gradient applied."
+ .to_owned(),
+ parameter_type: ParameterType::Boolean,
+ default_value: Some("true".to_string()),
+ optional: true,
+ });
+
+ parameters.push(ToolParameter {
+ name: "Flat increment value (z units)".to_owned(),
+ flags: vec!["--flat_increment".to_owned()],
+ description: "Optional elevation increment applied to flat areas.".to_owned(),
+ parameter_type: ParameterType::Float,
+ default_value: None,
+ optional: true,
+ });
+
+ let sep: String = path::MAIN_SEPARATOR.to_string();
+ let p = format!("{}", env::current_dir().unwrap().display());
+ let e = format!("{}", env::current_exe().unwrap().display());
+ let mut short_exe = e
+ .replace(&p, "")
+ .replace(".exe", "")
+ .replace(".", "")
+ .replace(&sep, "");
+ if e.contains(".exe") {
+ short_exe += ".exe";
+ }
+ let usage = format!(
+ ">>.*{0} -r={1} -v --wd=\"*path*to*data*\" --dem=DEM.tif -o=output.tif --fix_flats",
+ short_exe, name
+ )
+ .replace("*", &sep);
+
+ FillDepressionsWangAndLui {
+ name: name,
+ description: description,
+ toolbox: toolbox,
+ parameters: parameters,
+ example_usage: usage,
+ }
+ }
+}
+
+impl WhiteboxTool for FillDepressionsWangAndLui {
+ fn get_source_file(&self) -> String {
+ String::from(file!())
+ }
+
+ fn get_tool_name(&self) -> String {
+ self.name.clone()
+ }
+
+ fn get_tool_description(&self) -> String {
+ self.description.clone()
+ }
+
+ fn get_tool_parameters(&self) -> String {
+ match serde_json::to_string(&self.parameters) {
+ Ok(json_str) => return format!("{{\"parameters\":{}}}", json_str),
+ Err(err) => return format!("{:?}", err),
+ }
+ }
+
+ fn get_example_usage(&self) -> String {
+ self.example_usage.clone()
+ }
+
+ fn get_toolbox(&self) -> String {
+ self.toolbox.clone()
+ }
+
+ fn run<'a>(
+ &self,
+ args: Vec,
+ working_directory: &'a str,
+ verbose: bool,
+ ) -> Result<(), Error> {
+ let mut input_file = String::new();
+ let mut output_file = String::new();
+ let mut fix_flats = false;
+ let mut flat_increment = f64::NAN;
+
+ if args.len() == 0 {
+ return Err(Error::new(
+ ErrorKind::InvalidInput,
+ "Tool run with no paramters.",
+ ));
+ }
+ for i in 0..args.len() {
+ let mut arg = args[i].replace("\"", "");
+ arg = arg.replace("\'", "");
+ let cmd = arg.split("="); // in case an equals sign was used
+ let vec = cmd.collect::>();
+ let mut keyval = false;
+ if vec.len() > 1 {
+ keyval = true;
+ }
+ let flag_val = vec[0].to_lowercase().replace("--", "-");
+ if flag_val == "-i" || flag_val == "-input" || flag_val == "-dem"{
+ input_file = if keyval {
+ vec[1].to_string()
+ } else {
+ args[i + 1].to_string()
+ };
+ } else if flag_val == "-o" || flag_val == "-output" {
+ output_file = if keyval {
+ vec[1].to_string()
+ } else {
+ args[i + 1].to_string()
+ };
+ } else if flag_val == "-fix_flats" {
+ if vec.len() == 1 || !vec[1].to_string().to_lowercase().contains("false") {
+ fix_flats = true;
+ }
+ } else if flag_val == "-flat_increment" {
+ flat_increment = if keyval {
+ vec[1].to_string().parse::().unwrap()
+ } else {
+ args[i + 1].to_string().parse::().unwrap()
+ };
+ }
+ }
+
+ if verbose {
+ println!("***************{}", "*".repeat(self.get_tool_name().len()));
+ println!("* Welcome to {} *", self.get_tool_name());
+ println!("***************{}", "*".repeat(self.get_tool_name().len()));
+ }
+
+ let sep: String = path::MAIN_SEPARATOR.to_string();
+
+ let mut progress: usize;
+ let mut old_progress: usize = 1;
+
+ if !input_file.contains(&sep) && !input_file.contains("/") {
+ input_file = format!("{}{}", working_directory, input_file);
+ }
+ if !output_file.contains(&sep) && !output_file.contains("/") {
+ output_file = format!("{}{}", working_directory, output_file);
+ }
+
+ if verbose {
+ println!("Reading data...")
+ };
+
+ let input = Raster::new(&input_file, "r")?;
+
+ let start = Instant::now();
+ let rows = input.configs.rows as isize;
+ let columns = input.configs.columns as isize;
+ let num_cells = rows * columns;
+ let nodata = input.configs.nodata;
+
+ // let min_val = input.configs.minimum;
+ // let elev_digits = ((input.configs.maximum - min_val) as i64).to_string().len();
+ // let elev_multiplier = 10.0_f64.powi((7 - elev_digits) as i32);
+ // let mut small_num = 0.0;
+ // if fix_flats {
+ // small_num = 1.0 / elev_multiplier as f64;
+ // }
+
+ let small_num = if fix_flats && !flat_increment.is_nan() {
+ flat_increment
+ } else if fix_flats {
+ let min_val = input.configs.minimum;
+ let elev_digits = ((input.configs.maximum - min_val) as i64).to_string().len();
+ let elev_multiplier = 10.0_f64.powi((6 - elev_digits) as i32);
+ 1.0_f64 / elev_multiplier as f64
+ } else {
+ 0f64
+ };
+
+ let mut output = Raster::initialize_using_file(&output_file, &input);
+ output.configs.data_type = DataType::F64;
+ let background_val = (i32::min_value() + 1) as f64;
+ output.reinitialize_values(background_val);
+
+ /*
+ Find the data edges. This is complicated by the fact that DEMs frequently
+ have nodata edges, whereby the DEM does not occupy the full extent of
+ the raster. One approach to doing this would be simply to scan the
+ raster, looking for cells that neighbour nodata values. However, this
+ assumes that there are no interior nodata holes in the dataset. Instead,
+ the approach used here is to perform a region-growing operation, looking
+ for nodata values along the raster's edges.
+ */
+
+ let mut queue: VecDeque<(isize, isize)> =
+ VecDeque::with_capacity((rows * columns) as usize);
+ for row in 0..rows {
+ /*
+ Note that this is only possible because Whitebox rasters
+ allow you to address cells beyond the raster extent but
+ return the nodata value for these regions.
+ */
+ queue.push_back((row, -1));
+ queue.push_back((row, columns));
+ }
+
+ for col in 0..columns {
+ queue.push_back((-1, col));
+ queue.push_back((rows, col));
+ }
+
+ /*
+ minheap is the priority queue. Note that I've tested using integer-based
+ priority values, by multiplying the elevations, but this didn't result
+ in a significant performance gain over the use of f64s.
+ */
+ let mut minheap = BinaryHeap::with_capacity((rows * columns) as usize);
+ let mut num_solved_cells = 0;
+ let mut zin_n: f64; // value of neighbour of row, col in input raster
+ let mut zout: f64; // value of row, col in output raster
+ let mut zout_n: f64; // value of neighbour of row, col in output raster
+ let dx = [1, 1, 1, 0, -1, -1, -1, 0];
+ let dy = [-1, 0, 1, 1, 1, 0, -1, -1];
+ let (mut row, mut col): (isize, isize);
+ let (mut row_n, mut col_n): (isize, isize);
+ while !queue.is_empty() {
+ let cell = queue.pop_front().unwrap();
+ row = cell.0;
+ col = cell.1;
+ for n in 0..8 {
+ row_n = row + dy[n];
+ col_n = col + dx[n];
+ zin_n = input.get_value(row_n, col_n);
+ zout_n = output[(row_n, col_n)];
+ if zout_n == background_val {
+ if zin_n == nodata {
+ output.set_value(row_n, col_n, nodata);
+ queue.push_back((row_n, col_n));
+ } else {
+ output[(row_n, col_n)] = zin_n;
+ // Push it onto the priority queue for the priority flood operation
+ minheap.push(GridCell {
+ row: row_n,
+ column: col_n,
+ priority: zin_n,
+ });
+ }
+ num_solved_cells += 1;
+ }
+ }
+
+ if verbose {
+ progress = (100.0_f64 * num_solved_cells as f64 / (num_cells - 1) as f64) as usize;
+ if progress != old_progress {
+ println!("progress: {}%", progress);
+ old_progress = progress;
+ }
+ }
+ }
+
+ /*
+ The following code follows the scenario of a priority-flood method without the extra
+ complication of an embedded region-growing operation for in-depression sites.
+ */
+
+ // Perform the priority flood operation.
+ while !minheap.is_empty() {
+ let cell = minheap.pop().unwrap();
+ row = cell.row;
+ col = cell.column;
+ zout = output[(row, col)];
+ for n in 0..8 {
+ row_n = row + dy[n];
+ col_n = col + dx[n];
+ zout_n = output[(row_n, col_n)];
+ if zout_n == background_val {
+ zin_n = input[(row_n, col_n)];
+ if zin_n != nodata {
+ if zin_n < (zout + small_num) {
+ zin_n = zout + small_num;
+ } // We're in a depression. Raise the elevation.
+ output[(row_n, col_n)] = zin_n;
+ minheap.push(GridCell {
+ row: row_n,
+ column: col_n,
+ priority: zin_n,
+ });
+ } else {
+ // Interior nodata cells are still treated as nodata and are not filled.
+ output[(row_n, col_n)] = nodata;
+ num_solved_cells += 1;
+ }
+ }
+ }
+
+ if verbose {
+ num_solved_cells += 1;
+ progress = (100.0_f64 * num_solved_cells as f64 / (num_cells - 1) as f64) as usize;
+ if progress != old_progress {
+ println!("Progress: {}%", progress);
+ old_progress = progress;
+ }
+ }
+ }
+
+ /*
+ This code uses a slightly more complex priority flood approach that uses an embedded
+ region-growing operation for cells within depressions. It offers a slight speed up
+ over the traditional approach, but I have noticed that sometimes it doesn't work
+ as expected. I'm not sure why and it would require some effort to track down the bug.
+ Given that most DEMs have relatively few cells within depressions, the speed up of
+ this approach is perhaps not worthwhile and it is certainly more complex.
+ */
+ // // Perform the priority flood operation.
+ // while !minheap.is_empty() {
+ // let cell = minheap.pop().unwrap();
+ // row = cell.row;
+ // col = cell.column;
+ // zout = output[(row, col)];
+ // for n in 0..8 {
+ // row_n = row + dy[n];
+ // col_n = col + dx[n];
+ // zout_n = output[(row_n, col_n)];
+ // if zout_n == background_val {
+ // zin_n = input[(row_n, col_n)];
+ // if zin_n != nodata {
+ // if zin_n < (zout + small_num) {
+ // // We're in a depression. Raise the elevation.
+ // zout_n = zout + small_num;
+ // output[(row_n, col_n)] = zout_n;
+ // /*
+ // Cells that are in the depression don't need to be discovered by
+ // the more expensive priority-flood operation. Instead, perform
+ // an efficient region-growing operation to find cells connected
+ // to this cell that have elevations in the input DEM that are
+ // less than the adjusted zout_n.
+ // */
+ // queue.push_back((row_n, col_n));
+ // while !queue.is_empty() {
+ // let cell = queue.pop_front().unwrap();
+ // row = cell.0;
+ // col = cell.1;
+ // zout = output[(row, col)];
+ // for n2 in 0..8 {
+ // row_n = row + dy[n2];
+ // col_n = col + dx[n2];
+ // zout_n = output[(row_n, col_n)];
+ // if zout_n == background_val {
+ // zin_n = input[(row_n, col_n)];
+ // if zin_n != nodata {
+ // if zin_n < (zout + small_num) {
+ // zout_n = zout + small_num;
+ // output[(row_n, col_n)] = zout_n;
+ // queue.push_back((row_n, col_n));
+ // } else {
+ // minheap.push(GridCell{ row: row_n, column: col_n, priority: zin_n });
+ // output[(row_n, col_n)] = zin_n;
+ // }
+ // } else {
+ // // Interior nodata cells are still treated as nodata and are not filled.
+ // output[(row_n, col_n)] = nodata;
+ // num_solved_cells += 1;
+ // }
+ // }
+ // }
+ // if verbose {
+ // num_solved_cells += 1;
+ // progress = (100.0_f64 * num_solved_cells as f64 / (num_cells - 1) as f64) as usize;
+ // if progress != old_progress {
+ // println!("Progress: {}%", progress);
+ // old_progress = progress;
+ // }
+ // }
+ // }
+ // } else {
+ // minheap.push(GridCell{ row: row_n, column: col_n, priority: zin_n });
+ // output[(row_n, col_n)] = zin_n;
+ // }
+ // } else {
+ // // Interior nodata cells are still treated as nodata and are not filled.
+ // output[(row_n, col_n)] = nodata;
+ // num_solved_cells += 1;
+ // }
+ // }
+ // }
+
+ // if verbose {
+ // num_solved_cells += 1;
+ // progress = (100.0_f64 * num_solved_cells as f64 / (num_cells - 1) as f64) as usize;
+ // if progress != old_progress {
+ // println!("Progress: {}%", progress);
+ // old_progress = progress;
+ // }
+ // }
+ // }
+
+ /* This was an experiement with an approach that reduced the reliance on the priority queue.
+ It works well but is slightly less efficient that the traditional approach. */
+
+ // let mut output = Raster::initialize_using_file(&output_file, &input);
+ // let background_val = (i32::max_value() - 1) as f64;
+ // output.reinitialize_values(background_val);
+
+ // /*
+ // Find the data edges. This is complicated by the fact that DEMs frequently
+ // have nodata edges, whereby the DEM does not occupy the full extent of
+ // the raster. One approach to doing this would be simply to scan the
+ // raster, looking for cells that neighbour nodata values. However, this
+ // assumes that there are no interior nodata holes in the dataset. Instead,
+ // the approach used here is to perform a region-growing operation, looking
+ // for nodata values along the raster's edges.
+ // */
+ // //let mut stack = Vec::with_capacity((rows * columns) as usize);
+ // let mut queue: VecDeque<(isize, isize)> = VecDeque::with_capacity((rows * columns) as usize);
+ // for row in 0..rows {
+ // /*
+ // Note that this is only possible because Whitebox rasters
+ // allow you to address cells beyond the raster extent but
+ // return the nodata value for these regions.
+ // */
+ // queue.push_back((row, -1));
+ // queue.push_back((row, columns));
+ // }
+
+ // for col in 0..columns {
+ // queue.push_back((-1, col));
+ // queue.push_back((rows, col));
+ // }
+
+ // /*
+ // minheap is the priority queue. Note that I've tested using integer-based
+ // priority values, by multiplying the elevations, but this didn't result
+ // in a significant performance gain over the use of f64s.
+ // */
+ // let mut minheap = BinaryHeap::with_capacity((rows * columns) as usize);
+ // let mut num_solved_cells = 0;
+ // let mut zin_n: f64; // value of neighbour of row, col in input raster
+ // let mut zout: f64; // value of row, col in output raster
+ // let mut zout_n: f64; // value of neighbour of row, col in output raster
+ // let dx = [ 1, 1, 1, 0, -1, -1, -1, 0 ];
+ // let dy = [ -1, 0, 1, 1, 1, 0, -1, -1 ];
+ // let (mut row, mut col): (isize, isize);
+ // let (mut row_n, mut col_n): (isize, isize);
+ // while !queue.is_empty() {
+ // let cell = queue.pop_front().unwrap();
+ // row = cell.0;
+ // col = cell.1;
+ // for n in 0..8 {
+ // row_n = row + dy[n];
+ // col_n = col + dx[n];
+ // zin_n = input[(row_n, col_n)];
+ // zout_n = output[(row_n, col_n)];
+ // if zout_n == background_val {
+ // if zin_n == nodata {
+ // output[(row_n, col_n)] = nodata;
+ // queue.push_back((row_n, col_n));
+ // } else {
+ // output[(row_n, col_n)] = zin_n;
+ // // Push it onto the priority queue for the priority flood operation
+ // minheap.push(GridCell{ row: row_n, column: col_n, priority: zin_n });
+ // }
+ // num_solved_cells += 1;
+ // }
+ // }
+
+ // if verbose {
+ // progress = (100.0_f64 * num_solved_cells as f64 / (num_cells - 1) as f64) as usize;
+ // if progress != old_progress {
+ // println!("progress: {}%", progress);
+ // old_progress = progress;
+ // }
+ // }
+ // }
+
+ // // Perform the priority flood operation.
+ // // let initial_heap_size = minheap.len();
+ // // num_solved_cells = 0;
+ // while !minheap.is_empty() {
+ // let cell = minheap.pop().unwrap();
+ // queue.push_back((cell.row, cell.column));
+ // while !queue.is_empty() {
+ // let cell = queue.pop_front().unwrap();
+ // row = cell.0;
+ // col = cell.1;
+ // zout = output[(row, col)];
+ // for n in 0..8 {
+ // row_n = row + dy[n];
+ // col_n = col + dx[n];
+ // zout_n = output[(row_n, col_n)];
+ // zin_n = input[(row_n, col_n)];
+ // if zout_n > zin_n {
+ // if zin_n != nodata {
+ // if zin_n > zout + small_num {
+ // output[(row_n, col_n)] = zin_n;
+ // queue.push_back((row_n, col_n));
+ // num_solved_cells += 1;
+ // } else if zout + small_num < zout_n {
+ // output[(row_n, col_n)] = zout + small_num;
+ // queue.push_back((row_n, col_n));
+ // }
+ // } else {
+ // output[(row_n, col_n)] = nodata;
+ // queue.push_back((row_n, col_n));
+ // num_solved_cells += 1;
+ // }
+ // }
+ // }
+
+ // if verbose {
+ // progress = (100.0_f64 * num_solved_cells as f64 / (num_cells - 1) as f64) as usize;
+ // if progress != old_progress {
+ // println!("Progress: {}%", progress);
+ // old_progress = progress;
+ // }
+ // }
+ // }
+ // }
+
+ // if verbose { println!("Progress: 100%"); }
+
+ let elapsed_time = get_formatted_elapsed_time(start);
+ output.configs.display_min = input.configs.display_min;
+ output.configs.display_max = input.configs.display_max;
+ output.add_metadata_entry(format!(
+ "Created by whitebox_tools\' {} tool",
+ self.get_tool_name()
+ ));
+ output.add_metadata_entry(format!("Input file: {}", input_file));
+ output.add_metadata_entry(format!("Fix flats: {}", fix_flats));
+ if fix_flats {
+ output.add_metadata_entry(format!("Flat increment value: {}", small_num));
+ }
+ output.add_metadata_entry(format!("Elapsed Time (excluding I/O): {}", elapsed_time));
+
+ if verbose {
+ println!("Saving data...")
+ };
+ let _ = match output.write() {
+ Ok(_) => {
+ if verbose {
+ println!("Output file written")
+ }
+ }
+ Err(e) => return Err(e),
+ };
+ if verbose {
+ println!(
+ "{}",
+ &format!("Elapsed Time (excluding I/O): {}", elapsed_time)
+ );
+ }
+
+ Ok(())
+ }
+}
+
+#[derive(PartialEq, Debug)]
+struct GridCell {
+ row: isize,
+ column: isize,
+ priority: f64,
+}
+
+impl Eq for GridCell {}
+
+impl PartialOrd for GridCell {
+ fn partial_cmp(&self, other: &Self) -> Option {
+ other.priority.partial_cmp(&self.priority)
+ }
+}
+
+impl Ord for GridCell {
+ fn cmp(&self, other: &Self) -> Ordering {
+ self.partial_cmp(other).unwrap()
+ }
+}
diff --git a/src/tools/hydro_analysis/fill_pits.rs b/src/tools/hydro_analysis/fill_pits.rs
index 334d439a0..ca23fb79a 100644
--- a/src/tools/hydro_analysis/fill_pits.rs
+++ b/src/tools/hydro_analysis/fill_pits.rs
@@ -128,7 +128,7 @@ impl WhiteboxTool for FillSingleCellPits {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
@@ -206,7 +206,7 @@ impl WhiteboxTool for FillSingleCellPits {
flag = true;
min_zn = f64::INFINITY;
for n in 0..8 {
- zn = input[(row + dy[n], col + dx[n])];
+ zn = input.get_value(row + dy[n], col + dx[n]);
if zn < min_zn {
min_zn = zn;
}
diff --git a/src/tools/hydro_analysis/find_noflow_cells.rs b/src/tools/hydro_analysis/find_noflow_cells.rs
index 6c9cba633..cac87177d 100644
--- a/src/tools/hydro_analysis/find_noflow_cells.rs
+++ b/src/tools/hydro_analysis/find_noflow_cells.rs
@@ -127,7 +127,7 @@ impl WhiteboxTool for FindNoFlowCells {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
@@ -198,11 +198,11 @@ impl WhiteboxTool for FindNoFlowCells {
for row in (0..rows).filter(|r| r % num_procs == tid) {
let mut data = vec![nodata; columns as usize];
for col in 0..columns {
- z = input[(row, col)];
+ z = input.get_value(row, col);
if z != nodata {
has_no_lower_neighbour = 1.0;
for n in 0..8 {
- zn = input[(row + dy[n], col + dx[n])];
+ zn = input.get_value(row + dy[n], col + dx[n]);
if zn < z && zn != nodata {
has_no_lower_neighbour = nodata;
break;
diff --git a/src/tools/hydro_analysis/find_parallel_flow.rs b/src/tools/hydro_analysis/find_parallel_flow.rs
index 5728f2f1a..925990a34 100644
--- a/src/tools/hydro_analysis/find_parallel_flow.rs
+++ b/src/tools/hydro_analysis/find_parallel_flow.rs
@@ -138,7 +138,7 @@ impl WhiteboxTool for FindParallelFlow {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/hydro_analysis/flatten_lakes.rs b/src/tools/hydro_analysis/flatten_lakes.rs
index 0d2285e01..0d2d283b1 100644
--- a/src/tools/hydro_analysis/flatten_lakes.rs
+++ b/src/tools/hydro_analysis/flatten_lakes.rs
@@ -141,7 +141,7 @@ impl WhiteboxTool for FlattenLakes {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/hydro_analysis/flood_order.rs b/src/tools/hydro_analysis/flood_order.rs
index 7cb0682b5..4d76b7cc9 100644
--- a/src/tools/hydro_analysis/flood_order.rs
+++ b/src/tools/hydro_analysis/flood_order.rs
@@ -137,7 +137,7 @@ impl WhiteboxTool for FloodOrder {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/hydro_analysis/flow_accum_full_workflow.rs b/src/tools/hydro_analysis/flow_accum_full_workflow.rs
index f465b4961..70d3e017d 100644
--- a/src/tools/hydro_analysis/flow_accum_full_workflow.rs
+++ b/src/tools/hydro_analysis/flow_accum_full_workflow.rs
@@ -25,7 +25,8 @@ use std::sync::mpsc;
use std::sync::Arc;
use std::thread;
-/// Resolves all of the depressions in a DEM, outputting a breached DEM, an aspect-aligned non-divergent flow pointer, and a flow accumulation raster.
+/// Resolves all of the depressions in a DEM, outputting a breached DEM, an aspect-aligned non-divergent flow
+/// pointer, and a flow accumulation raster.
pub struct FlowAccumulationFullWorkflow {
name: String,
description: String,
@@ -187,7 +188,7 @@ impl WhiteboxTool for FlowAccumulationFullWorkflow {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
@@ -367,9 +368,8 @@ impl WhiteboxTool for FlowAccumulationFullWorkflow {
}
}
- let min_val = input.configs.minimum;
- let elev_digits = ((input.configs.maximum - min_val) as i64).to_string().len();
- let elev_multiplier = 10.0_f64.powi((7 - elev_digits) as i32);
+ let elev_digits = (input.configs.maximum as i64).to_string().len();
+ let elev_multiplier = 10.0_f64.powi((12 - elev_digits) as i32);
let small_num = 1.0 / elev_multiplier as f64;
let mut output = Raster::initialize_using_file(&outdem_file, &input);
diff --git a/src/tools/hydro_analysis/flow_length_diff.rs b/src/tools/hydro_analysis/flow_length_diff.rs
index 02bf9fd1e..788801e33 100644
--- a/src/tools/hydro_analysis/flow_length_diff.rs
+++ b/src/tools/hydro_analysis/flow_length_diff.rs
@@ -130,7 +130,7 @@ impl WhiteboxTool for FlowLengthDiff {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/hydro_analysis/hillslopes.rs b/src/tools/hydro_analysis/hillslopes.rs
index 932ffaab4..aca4720f5 100644
--- a/src/tools/hydro_analysis/hillslopes.rs
+++ b/src/tools/hydro_analysis/hillslopes.rs
@@ -153,7 +153,7 @@ impl WhiteboxTool for Hillslopes {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/hydro_analysis/impoundment_index.rs b/src/tools/hydro_analysis/impoundment_index.rs
index a2ee32167..ce3d6c58c 100644
--- a/src/tools/hydro_analysis/impoundment_index.rs
+++ b/src/tools/hydro_analysis/impoundment_index.rs
@@ -163,7 +163,7 @@ impl WhiteboxTool for ImpoundmentSizeIndex {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/hydro_analysis/isobasins.rs b/src/tools/hydro_analysis/isobasins.rs
index fbe13f34b..c09b566bb 100644
--- a/src/tools/hydro_analysis/isobasins.rs
+++ b/src/tools/hydro_analysis/isobasins.rs
@@ -140,7 +140,7 @@ impl WhiteboxTool for Isobasins {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/hydro_analysis/jenson_snap_pour_points.rs b/src/tools/hydro_analysis/jenson_snap_pour_points.rs
index 66f657985..00a66e09d 100644
--- a/src/tools/hydro_analysis/jenson_snap_pour_points.rs
+++ b/src/tools/hydro_analysis/jenson_snap_pour_points.rs
@@ -163,7 +163,7 @@ impl WhiteboxTool for JensonSnapPourPoints {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/hydro_analysis/longest_flowpath.rs b/src/tools/hydro_analysis/longest_flowpath.rs
index c04e40d11..1934f44f5 100644
--- a/src/tools/hydro_analysis/longest_flowpath.rs
+++ b/src/tools/hydro_analysis/longest_flowpath.rs
@@ -160,7 +160,7 @@ impl WhiteboxTool for LongestFlowpath {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/hydro_analysis/max_upslope_flowpath.rs b/src/tools/hydro_analysis/max_upslope_flowpath.rs
index 7a669480f..a26cf18e3 100644
--- a/src/tools/hydro_analysis/max_upslope_flowpath.rs
+++ b/src/tools/hydro_analysis/max_upslope_flowpath.rs
@@ -128,7 +128,7 @@ impl WhiteboxTool for MaxUpslopeFlowpathLength {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/hydro_analysis/mod.rs b/src/tools/hydro_analysis/mod.rs
index 60b7489a2..09c7bddb5 100644
--- a/src/tools/hydro_analysis/mod.rs
+++ b/src/tools/hydro_analysis/mod.rs
@@ -3,6 +3,7 @@ mod average_flowpath_slope;
mod average_upslope_flowpath_length;
mod basins;
mod breach_depressions;
+mod breach_depressions_least_cost;
mod breach_pits;
mod burn_streams_at_roads;
mod d8_flow_accum;
@@ -20,6 +21,7 @@ mod fd8_flow_accum;
mod fd8_pointer;
mod fill_burn;
mod fill_depressions;
+mod fill_depressions_wang_and_lui;
mod fill_pits;
mod find_noflow_cells;
mod find_parallel_flow;
@@ -43,6 +45,7 @@ mod strahler_basins;
mod subbasins;
mod trace_downslope_flowpaths;
mod unnest_basins;
+mod upslope_depression_storage;
mod watershed;
// exports identifiers from private sub-modules in the current module namespace
@@ -67,6 +70,7 @@ pub use self::fd8_flow_accum::FD8FlowAccumulation;
pub use self::fd8_pointer::FD8Pointer;
pub use self::fill_burn::FillBurn;
pub use self::fill_depressions::FillDepressions;
+pub use self::fill_depressions_wang_and_lui::FillDepressionsWangAndLui;
pub use self::fill_pits::FillSingleCellPits;
pub use self::find_noflow_cells::FindNoFlowCells;
pub use self::find_parallel_flow::FindParallelFlow;
@@ -78,6 +82,7 @@ pub use self::hillslopes::Hillslopes;
pub use self::impoundment_index::ImpoundmentSizeIndex;
pub use self::isobasins::Isobasins;
pub use self::jenson_snap_pour_points::JensonSnapPourPoints;
+pub use self::breach_depressions_least_cost::BreachDepressionsLeastCost;
pub use self::longest_flowpath::LongestFlowpath;
pub use self::max_upslope_flowpath::MaxUpslopeFlowpathLength;
pub use self::num_inflowing_neighbours::NumInflowingNeighbours;
@@ -90,4 +95,5 @@ pub use self::strahler_basins::StrahlerOrderBasins;
pub use self::subbasins::Subbasins;
pub use self::trace_downslope_flowpaths::TraceDownslopeFlowpaths;
pub use self::unnest_basins::UnnestBasins;
+pub use self::upslope_depression_storage::UpslopeDepressionStorage;
pub use self::watershed::Watershed;
diff --git a/src/tools/hydro_analysis/num_inflowing_neighbours.rs b/src/tools/hydro_analysis/num_inflowing_neighbours.rs
index 66d50f71c..4e77467cd 100644
--- a/src/tools/hydro_analysis/num_inflowing_neighbours.rs
+++ b/src/tools/hydro_analysis/num_inflowing_neighbours.rs
@@ -129,7 +129,7 @@ impl WhiteboxTool for NumInflowingNeighbours {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/hydro_analysis/raise_walls.rs b/src/tools/hydro_analysis/raise_walls.rs
index d9c797bda..6fb600603 100644
--- a/src/tools/hydro_analysis/raise_walls.rs
+++ b/src/tools/hydro_analysis/raise_walls.rs
@@ -154,7 +154,7 @@ impl WhiteboxTool for RaiseWalls {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/hydro_analysis/rho8_pointer.rs b/src/tools/hydro_analysis/rho8_pointer.rs
index 5522b71ab..6a95ced0c 100644
--- a/src/tools/hydro_analysis/rho8_pointer.rs
+++ b/src/tools/hydro_analysis/rho8_pointer.rs
@@ -157,7 +157,7 @@ impl WhiteboxTool for Rho8Pointer {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/hydro_analysis/sink.rs b/src/tools/hydro_analysis/sink.rs
index bf2528ac2..c17c9df38 100644
--- a/src/tools/hydro_analysis/sink.rs
+++ b/src/tools/hydro_analysis/sink.rs
@@ -2,21 +2,27 @@
This tool is part of the WhiteboxTools geospatial analysis library.
Authors: Dr. John Lindsay
Created: 01/07/2017
-Last Modified: 18/10/2019
+Last Modified: 05/12/2019
License: MIT
+
+Note: the previous iteration of this tool did not include the outlet cell itself as part of the depression.
+This iteration of the tool does include outlets.
*/
use crate::raster::*;
-use crate::structures::Array2D;
use crate::tools::*;
+use crate::structures::Array2D;
use std::cmp::Ordering;
-use std::collections::BinaryHeap;
-use std::collections::VecDeque;
+use std::cmp::Ordering::Equal;
+use std::collections::{BinaryHeap, VecDeque};
use std::env;
use std::f64;
use std::i32;
use std::io::{Error, ErrorKind};
use std::path;
+use std::sync::mpsc;
+use std::sync::Arc;
+use std::thread;
/// This tool identifies each sink (i.e. topographic depression) in a raster digital elevation model (DEM). A
/// sink, or depression, is a bowl-like landscape feature, which is characterized by interior drainage. Each
@@ -46,7 +52,7 @@ impl Sink {
let mut parameters = vec![];
parameters.push(ToolParameter {
name: "Input DEM File".to_owned(),
- flags: vec!["-i".to_owned(), "--dem".to_owned()],
+ flags: vec!["-i".to_owned(), "--dem".to_owned(), "--input".to_owned()],
description: "Input raster DEM file.".to_owned(),
parameter_type: ParameterType::ExistingFile(ParameterFileType::Raster),
default_value: None,
@@ -83,7 +89,7 @@ impl Sink {
if e.contains(".exe") {
short_exe += ".exe";
}
- let usage = format!(">>.*{0} -r={1} -v --wd=\"*path*to*data*\" --dem=DEM.tif -o=output.tif --zero_background", short_exe, name).replace("*", &sep);
+ let usage = format!(">>.*{0} -r={1} -v --wd=\"*path*to*data*\" --dem=DEM.tif -o=filled_dem.tif --zero_background", short_exe, name).replace("*", &sep);
Sink {
name: name,
@@ -136,7 +142,7 @@ impl WhiteboxTool for Sink {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
@@ -195,179 +201,241 @@ impl WhiteboxTool for Sink {
let start = Instant::now();
let rows = input.configs.rows as isize;
let columns = input.configs.columns as isize;
- let num_cells = rows * columns;
let nodata = input.configs.nodata;
- let mut output = Raster::initialize_using_file(&output_file, &input);
- let mut background_val = (i32::min_value() + 1) as f64;
- output.reinitialize_values(background_val);
-
- /*
- Find the data edges. This is complicated by the fact that DEMs frequently
- have nodata edges, whereby the DEM does not occupy the full extent of
- the raster. One approach to doing this would be simply to scan the
- raster, looking for cells that neighbour nodata values. However, this
- assumes that there are no interior nodata holes in the dataset. Instead,
- the approach used here is to perform a region-growing operation, looking
- for nodata values along the raster's edges.
- */
-
- let mut queue: VecDeque<(isize, isize)> =
- VecDeque::with_capacity((rows * columns) as usize);
- for row in 0..rows {
- /*
- Note that this is only possible because Whitebox rasters
- allow you to address cells beyond the raster extent but
- return the nodata value for these regions.
- */
- queue.push_back((row, -1));
- queue.push_back((row, columns));
- }
-
- for col in 0..columns {
- queue.push_back((-1, col));
- queue.push_back((rows, col));
- }
+ let mut filled_dem = input.get_data_as_array2d();
- /*
- minheap is the priority queue. Note that I've tested using integer-based
- priority values, by multiplying the elevations, but this didn't result
- in a significant performance gain over the use of f64s.
- */
- let mut minheap = BinaryHeap::with_capacity((rows * columns) as usize);
- let mut num_solved_cells = 0;
- let mut zin_n: f64; // value of neighbour of row, col in input raster
- let mut zout: f64; // value of row, col in output raster
- let mut zout_n: f64; // value of neighbour of row, col in output raster
+ let (mut col, mut row): (isize, isize);
+ let (mut rn, mut cn): (isize, isize);
+ let (mut z, mut zn): (f64, f64);
let dx = [1, 1, 1, 0, -1, -1, -1, 0];
let dy = [-1, 0, 1, 1, 1, 0, -1, -1];
- let (mut row, mut col): (isize, isize);
- let (mut row_n, mut col_n): (isize, isize);
- while !queue.is_empty() {
- let cell = queue.pop_front().unwrap();
- row = cell.0;
- col = cell.1;
- for n in 0..8 {
- row_n = row + dy[n];
- col_n = col + dx[n];
- zin_n = input[(row_n, col_n)];
- zout_n = output[(row_n, col_n)];
- if zout_n == background_val {
- if zin_n == nodata {
- output[(row_n, col_n)] = nodata;
- queue.push_back((row_n, col_n));
- } else {
- output[(row_n, col_n)] = zin_n;
- // Push it onto the priority queue for the priority flood operation
- minheap.push(GridCell {
- row: row_n,
- column: col_n,
- priority: zin_n,
- });
+
+ // Find pit cells. This step is parallelized.
+ let num_procs = num_cpus::get() as isize;
+ let filled_dem2 = Arc::new(filled_dem);
+ let (tx, rx) = mpsc::channel();
+ for tid in 0..num_procs {
+ let filled_dem2 = filled_dem2.clone();
+ let tx = tx.clone();
+ thread::spawn(move || {
+ let mut z: f64;
+ let mut zn: f64;
+ let mut flag: bool;
+ let mut pits = vec![];
+ for row in (1..rows-1).filter(|r| r % num_procs == tid) {
+ for col in 1..columns-1 {
+ z = filled_dem2.get_value(row, col);
+ if z != nodata {
+ flag = true;
+ for n in 0..8 {
+ zn = filled_dem2.get_value(row + dy[n], col + dx[n]);
+ if zn < z || zn == nodata { // It either has a lower neighbour or is an edge cell.
+ flag = false;
+ break;
+ }
+ }
+ if flag { // it's a cell with undefined flow
+ pits.push((row, col, z));
+ }
+ }
}
- num_solved_cells += 1;
}
- }
+ tx.send(pits).unwrap();
+ });
+ }
+ let mut undefined_flow_cells = vec![];
+ for p in 0..num_procs {
+ let mut pits = rx.recv().unwrap();
+ undefined_flow_cells.append(&mut pits);
+
if verbose {
- progress = (100.0_f64 * num_solved_cells as f64 / (num_cells - 1) as f64) as usize;
+ progress = (100.0_f64 * (p + 1) as f64 / num_procs as f64) as usize;
if progress != old_progress {
- println!("progress: {}%", progress);
+ println!("Finding pit cells: {}%", progress);
old_progress = progress;
}
}
}
- // Perform the priority flood operation.
- while !minheap.is_empty() {
- let cell = minheap.pop().unwrap();
- row = cell.row;
- col = cell.column;
- zout = output[(row, col)];
- for n in 0..8 {
- row_n = row + dy[n];
- col_n = col + dx[n];
- zout_n = output[(row_n, col_n)];
- if zout_n == background_val {
- zin_n = input[(row_n, col_n)];
- if zin_n != nodata {
- if zin_n < zout {
- zin_n = zout;
- } // We're in a depression. Raise the elevation.
- output[(row_n, col_n)] = zin_n;
- minheap.push(GridCell {
- row: row_n,
- column: col_n,
- priority: zin_n,
- });
+ let mut input_configs = input.configs.clone();
+ drop(input);
+
+ filled_dem = match Arc::try_unwrap(filled_dem2) {
+ Ok(val) => val,
+ Err(_) => panic!("Error unwrapping 'filled_dem'"),
+ };
+
+ let num_deps = undefined_flow_cells.len();
+
+ // Now we need to perform an in-place depression filling
+ let mut minheap = BinaryHeap::new();
+ let mut visited: Array2D = Array2D::new(rows, columns, 0, -1)?;
+ let mut flats: Array2D = Array2D::new(rows, columns, 0, -1)?;
+ let mut possible_outlets = vec![];
+ // solve from highest to lowest
+ undefined_flow_cells.sort_by(|a, b| a.2.partial_cmp(&b.2).unwrap_or(Equal));
+ let mut pit_id = 1;
+ let mut flag: bool;
+ while let Some(cell) = undefined_flow_cells.pop() {
+ row = cell.0;
+ col = cell.1;
+ if flats.get_value(row, col) != 1 { // if it's already in a solved site, don't do it a second time.
+ // First there is a priority region-growing operation to find the outlets.
+ z = filled_dem.get_value(row, col);
+ minheap.clear();
+ minheap.push(GridCell {
+ row: row,
+ column: col,
+ priority: z,
+ });
+ visited.set_value(row, col, 1);
+ let mut outlet_found = false;
+ let mut outlet_z = f64::INFINITY;
+ let mut queue = VecDeque::new();
+ while let Some(cell2) = minheap.pop() {
+ z = cell2.priority;
+ if outlet_found && z > outlet_z {
+ break;
+ }
+ if !outlet_found {
+ for n in 0..8 {
+ cn = cell2.column + dx[n];
+ rn = cell2.row + dy[n];
+ if visited.get_value(rn, cn) == 0 {
+ zn = filled_dem.get_value(rn, cn);
+ if !outlet_found {
+ if zn >= z && zn != nodata {
+ minheap.push(GridCell {
+ row: rn,
+ column: cn,
+ priority: zn,
+ });
+ visited.set_value(rn, cn, 1);
+ } else if zn != nodata { // zn < z
+ // 'cell' has a lower neighbour that hasn't already passed through minheap.
+ // Therefore, 'cell' is a pour point cell.
+ outlet_found = true;
+ outlet_z = z;
+ queue.push_back((cell2.row, cell2.column));
+ possible_outlets.push((cell2.row, cell2.column));
+ }
+ } else if zn == outlet_z { // We've found the outlet but are still looking for additional outlets.
+ minheap.push(GridCell {
+ row: rn,
+ column: cn,
+ priority: zn,
+ });
+ visited.set_value(rn, cn, 1);
+ }
+ }
+ }
} else {
- // Interior nodata cells are still treated as nodata and are not filled.
- output[(row_n, col_n)] = nodata;
- num_solved_cells += 1;
+ if z == outlet_z {
+ flag = false;
+ for n in 0..8 {
+ cn = cell2.column + dx[n];
+ rn = cell2.row + dy[n];
+ if visited.get_value(rn, cn) == 0 {
+ zn = filled_dem.get_value(rn, cn);
+ if zn < z {
+ flag = true;
+ } else if zn == outlet_z {
+ minheap.push(GridCell {
+ row: rn,
+ column: cn,
+ priority: zn,
+ });
+ visited.set_value(rn, cn, 1);
+ }
+ }
+ }
+ if flag { // it's an outlet
+ queue.push_back((cell2.row, cell2.column));
+ possible_outlets.push((cell2.row, cell2.column));
+ } else {
+ visited.set_value(cell2.row, cell2.column, 1);
+ }
+ }
+ }
+ }
+
+ // Now that we have the outlets, raise the interior of the depression
+ if outlet_found {
+ while let Some(cell2) = queue.pop_front() {
+ for n in 0..8 {
+ rn = cell2.0 + dy[n];
+ cn = cell2.1 + dx[n];
+ if visited.get_value(rn, cn) == 1 {
+ visited.set_value(rn, cn, 0);
+ queue.push_back((rn, cn));
+ z = filled_dem.get_value(rn, cn);
+ if z < outlet_z {
+ filled_dem.set_value(rn, cn, outlet_z);
+ flats.set_value(rn, cn, 1);
+ } else if z == outlet_z {
+ flats.set_value(rn, cn, 1);
+ }
+ }
+ }
}
}
}
if verbose {
- num_solved_cells += 1;
- progress = (100.0_f64 * num_solved_cells as f64 / (num_cells - 1) as f64) as usize;
+ progress = (100.0_f64 * pit_id as f64 / num_deps as f64) as usize;
if progress != old_progress {
- println!("Progress: {}%", progress);
+ println!("Finding depressions: {}%", progress);
old_progress = progress;
}
}
+ pit_id += 1;
}
- // Reclassify the output such that all cells that are higher than the input are identified.
- let mut fid = 0f64;
- background_val = nodata;
+ drop(visited);
+
+ input_configs.nodata = i32::MIN as f64;
+ let mut output = Raster::initialize_using_config(&output_file, &input_configs);
if zero_background {
- background_val = 0f64;
+ output.reinitialize_values(0f64);
}
- let mut visited: Array2D = Array2D::new(rows, columns, 0, -1)?;
- for row in 0..rows {
- for col in 0..columns {
- if output[(row, col)] > input[(row, col)] && visited[(row, col)] != 1 {
- fid += 1f64;
- output[(row, col)] = fid;
- visited[(row, col)] = 1;
- queue.push_back((row, col));
- while !queue.is_empty() {
- let cell = queue.pop_front().unwrap();
- for n in 0..8 {
- row_n = cell.0 + dy[n];
- col_n = cell.1 + dx[n];
- zout_n = output[(row_n, col_n)];
- zin_n = input[(row_n, col_n)];
- if zout_n > zin_n && visited[(row_n, col_n)] != 1 {
- output[(row_n, col_n)] = fid;
- visited[(row_n, col_n)] = 1;
- queue.push_back((row_n, col_n));
+ output.configs.data_type = DataType::I32;
+ output.configs.photometric_interp = PhotometricInterpretation::Categorical;
+ let mut dep_id = 1f64;
+ let num_outlets = possible_outlets.len();
+ while let Some(cell) = possible_outlets.pop() {
+ if flats.get_value(cell.0, cell.1) == 1 {
+ z = filled_dem.get_value(cell.0, cell.1);
+ output.set_value(cell.0, cell.1, dep_id);
+ let mut queue = VecDeque::new();
+ flats.set_value(cell.0, cell.1, 0);
+ queue.push_back((cell.0, cell.1, dep_id));
+ while let Some(cell2) = queue.pop_front() {
+ for n in 0..8 {
+ rn = cell2.0 + dy[n];
+ cn = cell2.1 + dx[n];
+ if flats.get_value(rn, cn) == 1 {
+ if filled_dem.get_value(rn, cn) == z {
+ flats.set_value(rn, cn, 0);
+ output.set_value(rn, cn, dep_id);
+ queue.push_back((rn, cn, dep_id));
}
}
}
- } else if output[(row, col)] == input[(row, col)] {
- visited[(row, col)] = 1;
- if input[(row, col)] != nodata {
- output[(row, col)] = background_val;
- } else {
- output[(row, col)] = nodata;
- }
}
+ dep_id += 1f64;
}
if verbose {
- progress = (100.0_f64 * row as f64 / (rows - 1) as f64) as usize;
+ progress = (100.0_f64 * (1.0 - possible_outlets.len() as f64 / num_outlets as f64)) as usize;
if progress != old_progress {
- println!("Clumping: {}%", progress);
+ println!("Labelling depressions: {}%", progress);
old_progress = progress;
}
}
}
let elapsed_time = get_formatted_elapsed_time(start);
- output.configs.data_type = DataType::F32;
- output.configs.palette = "qual.plt".to_string();
- output.configs.photometric_interp = PhotometricInterpretation::Categorical;
output.add_metadata_entry(format!(
"Created by whitebox_tools\' {} tool",
self.get_tool_name()
@@ -413,12 +481,7 @@ impl PartialOrd for GridCell {
}
impl Ord for GridCell {
- fn cmp(&self, other: &GridCell) -> Ordering {
- let ord = self.partial_cmp(other).unwrap();
- match ord {
- Ordering::Greater => Ordering::Less,
- Ordering::Less => Ordering::Greater,
- Ordering::Equal => ord,
- }
+ fn cmp(&self, other: &Self) -> Ordering {
+ self.partial_cmp(other).unwrap()
}
}
diff --git a/src/tools/hydro_analysis/snap_pour_points.rs b/src/tools/hydro_analysis/snap_pour_points.rs
index 023f8e770..144f7b441 100644
--- a/src/tools/hydro_analysis/snap_pour_points.rs
+++ b/src/tools/hydro_analysis/snap_pour_points.rs
@@ -160,7 +160,7 @@ impl WhiteboxTool for SnapPourPoints {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/hydro_analysis/stochastic_depression_analysis.rs b/src/tools/hydro_analysis/stochastic_depression_analysis.rs
index 633e5acb6..38bffbfea 100644
--- a/src/tools/hydro_analysis/stochastic_depression_analysis.rs
+++ b/src/tools/hydro_analysis/stochastic_depression_analysis.rs
@@ -202,7 +202,7 @@ impl WhiteboxTool for StochasticDepressionAnalysis {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/hydro_analysis/strahler_basins.rs b/src/tools/hydro_analysis/strahler_basins.rs
index 699c75fea..26508d949 100644
--- a/src/tools/hydro_analysis/strahler_basins.rs
+++ b/src/tools/hydro_analysis/strahler_basins.rs
@@ -146,7 +146,7 @@ impl WhiteboxTool for StrahlerOrderBasins {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/hydro_analysis/subbasins.rs b/src/tools/hydro_analysis/subbasins.rs
index e38cf6d2b..e97816c05 100644
--- a/src/tools/hydro_analysis/subbasins.rs
+++ b/src/tools/hydro_analysis/subbasins.rs
@@ -151,7 +151,7 @@ impl WhiteboxTool for Subbasins {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/hydro_analysis/trace_downslope_flowpaths.rs b/src/tools/hydro_analysis/trace_downslope_flowpaths.rs
index 824b7c818..47895522b 100644
--- a/src/tools/hydro_analysis/trace_downslope_flowpaths.rs
+++ b/src/tools/hydro_analysis/trace_downslope_flowpaths.rs
@@ -167,7 +167,7 @@ impl WhiteboxTool for TraceDownslopeFlowpaths {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/hydro_analysis/unnest_basins.rs b/src/tools/hydro_analysis/unnest_basins.rs
index afe0f5ea7..00b7c9689 100644
--- a/src/tools/hydro_analysis/unnest_basins.rs
+++ b/src/tools/hydro_analysis/unnest_basins.rs
@@ -151,7 +151,7 @@ impl WhiteboxTool for UnnestBasins {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/hydro_analysis/upslope_depression_storage.rs b/src/tools/hydro_analysis/upslope_depression_storage.rs
new file mode 100644
index 000000000..178cd4b2a
--- /dev/null
+++ b/src/tools/hydro_analysis/upslope_depression_storage.rs
@@ -0,0 +1,597 @@
+/*
+This tool is part of the WhiteboxTools geospatial analysis library.
+Authors: Dr. John Lindsay
+Created: 21/11/2019
+Last Modified: 21/11/2019
+License: MIT
+*/
+
+use crate::raster::*;
+use crate::structures::Array2D;
+use crate::tools::*;
+use num_cpus;
+use std::cmp::Ordering;
+use std::collections::BinaryHeap;
+use std::env;
+use std::f64;
+use std::f32;
+use std::io::{Error, ErrorKind};
+use std::path;
+use std::sync::mpsc;
+use std::sync::Arc;
+use std::thread;
+
+/// This tool estimates the average upslope depression storage depth using the FD8 flow algorithm.
+/// The input DEM (`--dem`) need not be hydrologically corrected; the tool will internally map depression
+/// storage and resolve flowpaths using depression filling. This input elevation model should be of a
+/// fine resolution (< 2 m), and is ideally derived using LiDAR. The tool calculates the total upslope
+/// depth of depression storage, which is divided by the number of upslope cells in the final step
+/// of the process, yielding the average upslope depression depth. Roughened surfaces tend to have higher
+/// values compared with smoothed surfaces. Values, particularly on hillslopes, may be very small (< 0.01 m).
+///
+/// # See Also
+/// `FD8FlowAccumulation`, `FillDepressions`, `DepthInSink`
+pub struct UpslopeDepressionStorage {
+ name: String,
+ description: String,
+ toolbox: String,
+ parameters: Vec,
+ example_usage: String,
+}
+
+impl UpslopeDepressionStorage {
+ pub fn new() -> UpslopeDepressionStorage {
+ // public constructor
+ let name = "UpslopeDepressionStorage".to_string();
+ let toolbox = "Hydrological Analysis".to_string();
+ let description = "Estimates the average upslope depression storage depth.".to_string();
+
+ let mut parameters = vec![];
+ parameters.push(ToolParameter {
+ name: "Input DEM File".to_owned(),
+ flags: vec!["-i".to_owned(), "--dem".to_owned()],
+ description: "Input raster DEM file.".to_owned(),
+ parameter_type: ParameterType::ExistingFile(ParameterFileType::Raster),
+ default_value: None,
+ optional: false,
+ });
+
+ parameters.push(ToolParameter {
+ name: "Output File".to_owned(),
+ flags: vec!["-o".to_owned(), "--output".to_owned()],
+ description: "Output raster file.".to_owned(),
+ parameter_type: ParameterType::NewFile(ParameterFileType::Raster),
+ default_value: None,
+ optional: false,
+ });
+
+ let sep: String = path::MAIN_SEPARATOR.to_string();
+ let p = format!("{}", env::current_dir().unwrap().display());
+ let e = format!("{}", env::current_exe().unwrap().display());
+ let mut short_exe = e
+ .replace(&p, "")
+ .replace(".exe", "")
+ .replace(".", "")
+ .replace(&sep, "");
+ if e.contains(".exe") {
+ short_exe += ".exe";
+ }
+ let usage = format!(
+ ">>.*{0} -r={1} -v --wd=\"*path*to*data*\" --dem=DEM.tif -o=output.tif",
+ short_exe, name
+ )
+ .replace("*", &sep);
+
+ UpslopeDepressionStorage {
+ name: name,
+ description: description,
+ toolbox: toolbox,
+ parameters: parameters,
+ example_usage: usage,
+ }
+ }
+}
+
+impl WhiteboxTool for UpslopeDepressionStorage {
+ fn get_source_file(&self) -> String {
+ String::from(file!())
+ }
+
+ fn get_tool_name(&self) -> String {
+ self.name.clone()
+ }
+
+ fn get_tool_description(&self) -> String {
+ self.description.clone()
+ }
+
+ fn get_tool_parameters(&self) -> String {
+ match serde_json::to_string(&self.parameters) {
+ Ok(json_str) => return format!("{{\"parameters\":{}}}", json_str),
+ Err(err) => return format!("{:?}", err),
+ }
+ }
+
+ fn get_example_usage(&self) -> String {
+ self.example_usage.clone()
+ }
+
+ fn get_toolbox(&self) -> String {
+ self.toolbox.clone()
+ }
+
+ fn run<'a>(
+ &self,
+ args: Vec,
+ working_directory: &'a str,
+ verbose: bool,
+ ) -> Result<(), Error> {
+ let mut input_file = String::new();
+ let mut output_file = String::new();
+
+ if args.len() == 0 {
+ return Err(Error::new(
+ ErrorKind::InvalidInput,
+ "Tool run with no parameters.",
+ ));
+ }
+ for i in 0..args.len() {
+ let mut arg = args[i].replace("\"", "");
+ arg = arg.replace("\'", "");
+ let cmd = arg.split("="); // in case an equals sign was used
+ let vec = cmd.collect::>();
+ let mut keyval = false;
+ if vec.len() > 1 {
+ keyval = true;
+ }
+ let flag_val = vec[0].to_lowercase().replace("--", "-");
+ if flag_val == "-i" || flag_val == "-input" || flag_val == "-dem" {
+ input_file = if keyval {
+ vec[1].to_string()
+ } else {
+ args[i + 1].to_string()
+ };
+ } else if flag_val == "-o" || flag_val == "-output" {
+ output_file = if keyval {
+ vec[1].to_string()
+ } else {
+ args[i + 1].to_string()
+ };
+ }
+ }
+
+ if verbose {
+ println!("***************{}", "*".repeat(self.get_tool_name().len()));
+ println!("* Welcome to {} *", self.get_tool_name());
+ println!("***************{}", "*".repeat(self.get_tool_name().len()));
+ }
+
+ let sep: String = path::MAIN_SEPARATOR.to_string();
+
+ let mut progress: usize;
+ let mut old_progress: usize = 1;
+
+ if !input_file.contains(&sep) && !input_file.contains("/") {
+ input_file = format!("{}{}", working_directory, input_file);
+ }
+ if !output_file.contains(&sep) && !output_file.contains("/") {
+ output_file = format!("{}{}", working_directory, output_file);
+ }
+
+ if verbose {
+ println!("Reading data...")
+ };
+
+ let input = Raster::new(&input_file, "r")?;
+
+ let start = Instant::now();
+
+ let rows = input.configs.rows as isize;
+ let columns = input.configs.columns as isize;
+ // let cell_area = input.configs.resolution_x * input.configs.resolution_y;
+ let (mut col, mut row): (isize, isize);
+ let (mut rn, mut cn): (isize, isize);
+ let mut z: f32;
+ let mut zn: f32;
+ let dx = [1, 1, 1, 0, -1, -1, -1, 0];
+ let dy = [-1, 0, 1, 1, 1, 0, -1, -1];
+ // let back_link = [4i8, 5i8, 6i8, 7i8, 0i8, 1i8, 2i8, 3i8];
+ let mut num_solved: usize;
+
+ // let cell_size_x = input.configs.resolution_x as f32;
+ // let cell_size_y = input.configs.resolution_y as f32;
+ // let diag_cell_size = (cell_size_x * cell_size_x + cell_size_y * cell_size_y).sqrt();
+ // let grid_lengths = [
+ // diag_cell_size,
+ // cell_size_x,
+ // diag_cell_size,
+ // cell_size_y,
+ // diag_cell_size,
+ // cell_size_x,
+ // diag_cell_size,
+ // cell_size_y,
+ // ];
+
+ let mut filled = input.get_data_as_f32_array2d();
+ let nodata = filled.nodata();
+
+ let elev_digits = (input.configs.maximum as i64).to_string().len();
+ let elev_multiplier = 10.0_f64.powi((6 - elev_digits) as i32);
+ let small_num = 1.0_f32 / elev_multiplier as f32;
+
+ let mut output = Raster::initialize_using_file(&output_file, &input);
+
+ // drop(input); // input is no longer needed.
+
+ // Now we need to perform an in-place depression filling
+
+ // Start by finding all cells that neighbour NoData cells.
+ let mut visited: Array2D = Array2D::new(rows, columns, 0, -1)?;
+ // let mut flow_dir: Array2D = Array2D::new(rows, columns, -1, -1)?;
+ let mut minheap = BinaryHeap::with_capacity((rows * columns) as usize);
+ let mut num_cells_visited = 0;
+ for row in 0..rows {
+ for col in 0..columns {
+ z = filled.get_value(row, col);
+ if z != nodata {
+ for n in 0..8 {
+ if filled.get_value(row + dy[n], col + dx[n]) == nodata {
+ minheap.push(GridCell {
+ row: row,
+ column: col,
+ priority: z,
+ });
+ visited.set_value(row, col, 1);
+ output.set_value(row, col, 0f64);
+ num_cells_visited += 1;
+ break;
+ }
+ }
+ } else {
+ visited.set_value(row, col, 1);
+ num_cells_visited += 1;
+ }
+ }
+ if verbose {
+ progress = (100.0_f64 * row as f64 / (rows - 1) as f64) as usize;
+ if progress != old_progress {
+ println!("Finding edges: {}%", progress);
+ old_progress = progress;
+ }
+ }
+ }
+
+ while !minheap.is_empty() {
+ let cell = minheap.pop().unwrap();
+ row = cell.row;
+ col = cell.column;
+ z = filled.get_value(row, col);
+ for n in 0..8 {
+ rn = row + dy[n];
+ cn = col + dx[n];
+ if visited.get_value(rn, cn) == 0i8 {
+ zn = filled.get_value(rn, cn);
+ if zn < (z + small_num) {
+ // output.set_value(rn, cn, (z - zn) as f64); // * cell_area);
+ if (zn as f64) < (input.get_value(row, col) + output.get_value(row, col)) {
+ output.set_value(rn, cn, (input.get_value(row, col) + output.get_value(row, col)) - zn as f64);
+ } else {
+ output.set_value(rn, cn, 0f64);
+ }
+ filled.set_value(rn, cn, z + small_num);
+ } else {
+ output.set_value(rn, cn, 0f64);
+ }
+ minheap.push(GridCell {
+ row: rn,
+ column: cn,
+ priority: zn,
+ });
+ visited.set_value(rn, cn, 1);
+ // flow_dir.set_value(rn, cn, back_link[n]);
+ num_cells_visited += 1;
+ }
+ }
+ if verbose {
+ progress = (100.0_f64 * num_cells_visited as f64 / (rows*columns - 1) as f64) as usize;
+ if progress != old_progress {
+ println!("Filling: {}%", progress);
+ old_progress = progress;
+ }
+ }
+ }
+ drop(input);
+ drop(visited);
+
+ // let mut num_inflowing: Array2D = Array2D::new(rows, columns, -1, -1)?;
+ // let filled = Arc::new(filled);
+ // let flow_dir = Arc::new(flow_dir);
+ // let num_procs = num_cpus::get() as isize;
+ // let (tx, rx) = mpsc::channel();
+ // for tid in 0..num_procs {
+ // let filled = filled.clone();
+ // let flow_dir = flow_dir.clone();
+ // let tx = tx.clone();
+ // thread::spawn(move || {
+ // let dx = [1, 1, 1, 0, -1, -1, -1, 0];
+ // let dy = [-1, 0, 1, 1, 1, 0, -1, -1];
+ // let inflowing_vals: [i8; 8] = [4, 5, 6, 7, 0, 1, 2, 3];
+ // let mut count: i8;
+ // for row in (0..rows).filter(|r| r % num_procs == tid) {
+ // let mut data: Vec = vec![-1i8; columns as usize];
+ // for col in 0..columns {
+ // if filled.get_value(row, col) != nodata {
+ // count = 0i8;
+ // for i in 0..8 {
+ // if flow_dir.get_value(row + dy[i], col + dx[i]) == inflowing_vals[i] {
+ // count += 1;
+ // }
+ // }
+ // data[col as usize] = count;
+ // } else {
+ // data[col as usize] = -1i8;
+ // }
+ // }
+ // tx.send((row, data)).unwrap();
+ // }
+ // });
+ // }
+
+ // let mut stack = Vec::with_capacity((rows * columns) as usize);
+ // num_solved = 0;
+ // for r in 0..rows {
+ // let (row, data) = rx.recv().unwrap();
+ // num_inflowing.set_row_data(row, data);
+ // for col in 0..columns {
+ // if num_inflowing.get_value(row, col) == 0i8 {
+ // stack.push((row, col));
+ // } else if num_inflowing[(row, col)] == -1i8 {
+ // num_solved += 1;
+ // }
+ // }
+
+ // if verbose {
+ // progress = (100.0_f64 * r as f64 / (rows - 1) as f64) as usize;
+ // if progress != old_progress {
+ // println!("Num. inflowing neighbours: {}%", progress);
+ // old_progress = progress;
+ // }
+ // }
+ // }
+
+ // let mut area: Array2D = Array2D::new(rows, columns, 1, -1)?;
+ // let mut fa: f64;
+ // let mut fa2: i32;
+ // let mut dir: i8;
+ // while !stack.is_empty() {
+ // let cell = stack.pop().unwrap();
+ // row = cell.0;
+ // col = cell.1;
+ // fa = output.get_value(row, col);
+ // fa2 = area.get_value(row, col);
+ // num_inflowing.decrement(row, col, 1i8);
+ // dir = flow_dir.get_value(row, col);
+ // if dir >= 0 {
+ // rn = row + dy[dir as usize];
+ // cn = col + dx[dir as usize];
+ // output.increment(rn, cn, fa);
+ // area.increment(rn, cn, fa2);
+ // num_inflowing.decrement(rn, cn, 1i8);
+ // if num_inflowing.get_value(rn, cn) == 0i8 {
+ // stack.push((rn, cn));
+ // }
+ // }
+
+ // if verbose {
+ // num_solved += 1;
+ // progress = (100.0_f64 * num_solved as f64 / (rows*columns - 1) as f64) as usize;
+ // if progress != old_progress {
+ // println!("Flow accumulation: {}%", progress);
+ // old_progress = progress;
+ // }
+ // }
+ // }
+
+ // for row in 0..rows {
+ // for col in 0..columns {
+ // z = filled.get_value(row, col);
+ // if z != nodata {
+ // output.set_value(row, col, output.get_value(row, col) / area.get_value(row, col) as f64);
+ // }
+ // }
+ // if verbose {
+ // progress = (100.0_f64 * num_cells_visited as f64 / (rows*columns - 1) as f64) as usize;
+ // if progress != old_progress {
+ // println!("Final calculation: {}%", progress);
+ // old_progress = progress;
+ // }
+ // }
+ // }
+
+
+ // calculate the number of inflowing cells
+ let filled = Arc::new(filled);
+ let mut num_inflowing: Array2D = Array2D::new(rows, columns, -1, -1)?;
+ let num_procs = num_cpus::get() as isize;
+ let (tx, rx) = mpsc::channel();
+ for tid in 0..num_procs {
+ let filled = filled.clone();
+ let tx = tx.clone();
+ thread::spawn(move || {
+ let dx = [1, 1, 1, 0, -1, -1, -1, 0];
+ let dy = [-1, 0, 1, 1, 1, 0, -1, -1];
+ let mut z: f32;
+ let mut zn: f32;
+ let mut count: i8;
+ for row in (0..rows).filter(|r| r % num_procs == tid) {
+ let mut data: Vec = vec![-1i8; columns as usize];
+ for col in 0..columns {
+ z = filled.get_value(row, col);
+ if z != nodata {
+ count = 0i8;
+ for n in 0..8 {
+ zn = filled.get_value(row + dy[n], col + dx[n]);
+ if zn > z && zn != nodata {
+ count += 1;
+ }
+ }
+ data[col as usize] = count;
+ }
+ }
+ tx.send((row, data)).unwrap();
+ }
+ });
+ }
+
+ let mut stack = Vec::with_capacity((rows * columns) as usize);
+ num_solved = 0;
+ for r in 0..rows {
+ let (row, data) = rx.recv().unwrap();
+ num_inflowing.set_row_data(row, data);
+ for col in 0..columns {
+ if num_inflowing.get_value(row, col) == 0i8 {
+ stack.push((row, col));
+ } else if num_inflowing.get_value(row, col) == -1i8 {
+ num_solved += 1;
+ }
+ }
+
+ if verbose {
+ progress = (100.0_f64 * r as f64 / (rows - 1) as f64) as usize;
+ if progress != old_progress {
+ println!("Num. inflowing neighbours: {}%", progress);
+ old_progress = progress;
+ }
+ }
+ }
+
+ let mut fa: f64;
+ let mut fa2: f64;
+ let mut area: Array2D = Array2D::new(rows, columns, 1f64, -1f64)?;
+ // let (mut max_slope, mut slope): (f32, f32);
+ // let mut dir: i8;
+ // let convergence_threshold = f64::INFINITY;
+ let exponent = 1.1f32;
+ let mut total_weights: f32;
+ let mut weights: [f32; 8] = [0.0; 8];
+ let mut downslope: [bool; 8] = [false; 8];
+ while !stack.is_empty() {
+ let cell = stack.pop().unwrap();
+ row = cell.0;
+ col = cell.1;
+ z = filled.get_value(row, col);
+ fa = output.get_value(row, col);
+ fa2 = area.get_value(row, col);
+ num_inflowing.set_value(row, col, -1i8);
+ total_weights = 0.0f32;
+ for n in 0..8 {
+ rn = row + dy[n];
+ cn = col + dx[n];
+ zn = filled.get_value(rn, cn);
+ if zn < z && zn != nodata {
+ weights[n] = (z - zn).powf(exponent);
+ total_weights += weights[n];
+ downslope[n] = true;
+ } else {
+ weights[n] = 0f32;
+ downslope[n] = false;
+ }
+ }
+
+ if total_weights > 0.0 {
+ for n in 0..8 {
+ if downslope[n] {
+ rn = row + dy[n];
+ cn = col + dx[n];
+ output.increment(rn, cn, fa * (weights[n] / total_weights) as f64);
+ area.increment(rn, cn, fa2 * (weights[n] / total_weights) as f64);
+ num_inflowing.decrement(rn, cn, 1i8);
+ if num_inflowing.get_value(rn, cn) == 0i8 {
+ stack.push((rn, cn));
+ }
+ }
+ }
+ }
+
+ if verbose {
+ num_solved += 1;
+ progress = (100.0_f64 * num_solved as f64 / (rows*columns - 1) as f64) as usize;
+ if progress != old_progress {
+ println!("Flow accumulation: {}%", progress);
+ old_progress = progress;
+ }
+ }
+ }
+
+ for row in 0..rows {
+ for col in 0..columns {
+ z = filled.get_value(row, col);
+ if z != nodata {
+ output.set_value(row, col, output.get_value(row, col) / area.get_value(row, col));
+ }
+ }
+ if verbose {
+ progress = (100.0_f64 * num_cells_visited as f64 / (rows*columns - 1) as f64) as usize;
+ if progress != old_progress {
+ println!("Final calculation: {}%", progress);
+ old_progress = progress;
+ }
+ }
+ }
+
+ let elapsed_time = get_formatted_elapsed_time(start);
+ output.add_metadata_entry(format!(
+ "Created by whitebox_tools\' {} tool",
+ self.get_tool_name()
+ ));
+ output.add_metadata_entry(format!("Input file: {}", input_file));
+ output.add_metadata_entry(format!("Elapsed Time (excluding I/O): {}", elapsed_time));
+
+ if verbose {
+ println!("Saving data...")
+ };
+ let _ = match output.write() {
+ Ok(_) => {
+ if verbose {
+ println!("Output file written")
+ }
+ }
+ Err(e) => return Err(e),
+ };
+ if verbose {
+ println!(
+ "{}",
+ &format!("Elapsed Time (excluding I/O): {}", elapsed_time)
+ );
+ }
+
+ Ok(())
+ }
+}
+
+#[derive(PartialEq, Debug)]
+struct GridCell {
+ row: isize,
+ column: isize,
+ priority: f32,
+}
+
+impl Eq for GridCell {}
+
+impl PartialOrd for GridCell {
+ fn partial_cmp(&self, other: &Self) -> Option {
+ other.priority.partial_cmp(&self.priority)
+ }
+}
+
+impl Ord for GridCell {
+ fn cmp(&self, other: &GridCell) -> Ordering {
+ // other.priority.cmp(&self.priority)
+ let ord = self.partial_cmp(other).unwrap();
+ match ord {
+ Ordering::Greater => Ordering::Less,
+ Ordering::Less => Ordering::Greater,
+ Ordering::Equal => ord,
+ }
+ }
+}
diff --git a/src/tools/hydro_analysis/watershed.rs b/src/tools/hydro_analysis/watershed.rs
index 5d1eb36c2..bf21effb6 100644
--- a/src/tools/hydro_analysis/watershed.rs
+++ b/src/tools/hydro_analysis/watershed.rs
@@ -173,7 +173,7 @@ impl WhiteboxTool for Watershed {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/image_analysis/adaptive_filter.rs b/src/tools/image_analysis/adaptive_filter.rs
index f90ea333b..1a3d30dac 100644
--- a/src/tools/image_analysis/adaptive_filter.rs
+++ b/src/tools/image_analysis/adaptive_filter.rs
@@ -159,7 +159,7 @@ impl WhiteboxTool for AdaptiveFilter {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/image_analysis/balance_contrast_enhancement.rs b/src/tools/image_analysis/balance_contrast_enhancement.rs
index 6d7f3b909..df3a62421 100644
--- a/src/tools/image_analysis/balance_contrast_enhancement.rs
+++ b/src/tools/image_analysis/balance_contrast_enhancement.rs
@@ -141,7 +141,7 @@ impl WhiteboxTool for BalanceContrastEnhancement {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/image_analysis/bilateral_filter.rs b/src/tools/image_analysis/bilateral_filter.rs
index 35eb03a18..95243d8fb 100644
--- a/src/tools/image_analysis/bilateral_filter.rs
+++ b/src/tools/image_analysis/bilateral_filter.rs
@@ -159,7 +159,7 @@ impl WhiteboxTool for BilateralFilter {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/image_analysis/change_vector_analysis.rs b/src/tools/image_analysis/change_vector_analysis.rs
index 1b7d3954b..8453a3ebe 100644
--- a/src/tools/image_analysis/change_vector_analysis.rs
+++ b/src/tools/image_analysis/change_vector_analysis.rs
@@ -162,7 +162,7 @@ impl WhiteboxTool for ChangeVectorAnalysis {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/image_analysis/closing.rs b/src/tools/image_analysis/closing.rs
index 2924a53ac..56181fcc7 100644
--- a/src/tools/image_analysis/closing.rs
+++ b/src/tools/image_analysis/closing.rs
@@ -148,7 +148,7 @@ impl WhiteboxTool for Closing {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/image_analysis/conservative_smoothing_filter.rs b/src/tools/image_analysis/conservative_smoothing_filter.rs
index 7b697008f..8afb1f8bb 100644
--- a/src/tools/image_analysis/conservative_smoothing_filter.rs
+++ b/src/tools/image_analysis/conservative_smoothing_filter.rs
@@ -154,7 +154,7 @@ impl WhiteboxTool for ConservativeSmoothingFilter {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/image_analysis/corner_detection.rs b/src/tools/image_analysis/corner_detection.rs
index 7b8fb7b44..fc4cc9216 100644
--- a/src/tools/image_analysis/corner_detection.rs
+++ b/src/tools/image_analysis/corner_detection.rs
@@ -128,7 +128,7 @@ impl WhiteboxTool for CornerDetection {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/image_analysis/correct_vignetting.rs b/src/tools/image_analysis/correct_vignetting.rs
index 86982f9d4..e5b4ea5c2 100644
--- a/src/tools/image_analysis/correct_vignetting.rs
+++ b/src/tools/image_analysis/correct_vignetting.rs
@@ -176,7 +176,7 @@ impl WhiteboxTool for CorrectVignetting {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/image_analysis/create_colour_composite.rs b/src/tools/image_analysis/create_colour_composite.rs
index 9b0e42b67..ce239ddb3 100644
--- a/src/tools/image_analysis/create_colour_composite.rs
+++ b/src/tools/image_analysis/create_colour_composite.rs
@@ -193,7 +193,7 @@ impl WhiteboxTool for CreateColourComposite {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/image_analysis/direct_decorrelation_stretch.rs b/src/tools/image_analysis/direct_decorrelation_stretch.rs
index f0bd89dab..4a464f3e6 100644
--- a/src/tools/image_analysis/direct_decorrelation_stretch.rs
+++ b/src/tools/image_analysis/direct_decorrelation_stretch.rs
@@ -162,7 +162,7 @@ impl WhiteboxTool for DirectDecorrelationStretch {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/image_analysis/diversity_filter.rs b/src/tools/image_analysis/diversity_filter.rs
index 4d31cd170..32ce0a62d 100644
--- a/src/tools/image_analysis/diversity_filter.rs
+++ b/src/tools/image_analysis/diversity_filter.rs
@@ -146,7 +146,7 @@ impl WhiteboxTool for DiversityFilter {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/image_analysis/dog_filter.rs b/src/tools/image_analysis/dog_filter.rs
index 09d9c4712..015638427 100644
--- a/src/tools/image_analysis/dog_filter.rs
+++ b/src/tools/image_analysis/dog_filter.rs
@@ -162,7 +162,7 @@ impl WhiteboxTool for DiffOfGaussianFilter {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/image_analysis/edge_preserving_mean_filter.rs b/src/tools/image_analysis/edge_preserving_mean_filter.rs
index 0d09b1571..d6437fa66 100644
--- a/src/tools/image_analysis/edge_preserving_mean_filter.rs
+++ b/src/tools/image_analysis/edge_preserving_mean_filter.rs
@@ -159,7 +159,7 @@ impl WhiteboxTool for EdgePreservingMeanFilter {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/image_analysis/emboss_filter.rs b/src/tools/image_analysis/emboss_filter.rs
index a791a6a4c..5dca821c3 100644
--- a/src/tools/image_analysis/emboss_filter.rs
+++ b/src/tools/image_analysis/emboss_filter.rs
@@ -207,7 +207,7 @@ impl WhiteboxTool for EmbossFilter {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
diff --git a/src/tools/image_analysis/fast_almost_gaussian_filter.rs b/src/tools/image_analysis/fast_almost_gaussian_filter.rs
index 7ba89bcab..0f9de7ccc 100644
--- a/src/tools/image_analysis/fast_almost_gaussian_filter.rs
+++ b/src/tools/image_analysis/fast_almost_gaussian_filter.rs
@@ -137,7 +137,7 @@ impl WhiteboxTool for FastAlmostGaussianFilter {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/image_analysis/flip_image.rs b/src/tools/image_analysis/flip_image.rs
index 59a9ab326..8550877be 100644
--- a/src/tools/image_analysis/flip_image.rs
+++ b/src/tools/image_analysis/flip_image.rs
@@ -131,7 +131,7 @@ impl WhiteboxTool for FlipImage {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/image_analysis/gamma_correction.rs b/src/tools/image_analysis/gamma_correction.rs
index 6a4246df9..fb8e35258 100644
--- a/src/tools/image_analysis/gamma_correction.rs
+++ b/src/tools/image_analysis/gamma_correction.rs
@@ -136,7 +136,7 @@ impl WhiteboxTool for GammaCorrection {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/image_analysis/gaussian_contrast_stretch.rs b/src/tools/image_analysis/gaussian_contrast_stretch.rs
index 51a593523..b11c9335f 100644
--- a/src/tools/image_analysis/gaussian_contrast_stretch.rs
+++ b/src/tools/image_analysis/gaussian_contrast_stretch.rs
@@ -152,7 +152,7 @@ impl WhiteboxTool for GaussianContrastStretch {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/image_analysis/gaussian_filter.rs b/src/tools/image_analysis/gaussian_filter.rs
index d1d23174c..e00a97b30 100644
--- a/src/tools/image_analysis/gaussian_filter.rs
+++ b/src/tools/image_analysis/gaussian_filter.rs
@@ -153,7 +153,7 @@ impl WhiteboxTool for GaussianFilter {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/image_analysis/highpass_filter.rs b/src/tools/image_analysis/highpass_filter.rs
index a3795faca..ac8cb368a 100644
--- a/src/tools/image_analysis/highpass_filter.rs
+++ b/src/tools/image_analysis/highpass_filter.rs
@@ -148,7 +148,7 @@ impl WhiteboxTool for HighPassFilter {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/image_analysis/highpass_median_filter.rs b/src/tools/image_analysis/highpass_median_filter.rs
index f6faf7ff4..9c5ee1d52 100644
--- a/src/tools/image_analysis/highpass_median_filter.rs
+++ b/src/tools/image_analysis/highpass_median_filter.rs
@@ -168,7 +168,7 @@ impl WhiteboxTool for HighPassMedianFilter {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/image_analysis/histogram_equalization.rs b/src/tools/image_analysis/histogram_equalization.rs
index 4dc612921..4ba5dc49f 100644
--- a/src/tools/image_analysis/histogram_equalization.rs
+++ b/src/tools/image_analysis/histogram_equalization.rs
@@ -160,7 +160,7 @@ impl WhiteboxTool for HistogramEqualization {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/image_analysis/histogram_matching.rs b/src/tools/image_analysis/histogram_matching.rs
index 86c855589..a2c7712a6 100644
--- a/src/tools/image_analysis/histogram_matching.rs
+++ b/src/tools/image_analysis/histogram_matching.rs
@@ -164,7 +164,7 @@ impl WhiteboxTool for HistogramMatching {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/image_analysis/histogram_matching_two_images.rs b/src/tools/image_analysis/histogram_matching_two_images.rs
index 30689f892..a948adc93 100644
--- a/src/tools/image_analysis/histogram_matching_two_images.rs
+++ b/src/tools/image_analysis/histogram_matching_two_images.rs
@@ -148,7 +148,7 @@ impl WhiteboxTool for HistogramMatchingTwoImages {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/image_analysis/ihs_to_rgb.rs b/src/tools/image_analysis/ihs_to_rgb.rs
index 69732b3f3..282ec2a15 100644
--- a/src/tools/image_analysis/ihs_to_rgb.rs
+++ b/src/tools/image_analysis/ihs_to_rgb.rs
@@ -208,7 +208,7 @@ impl WhiteboxTool for IhsToRgb {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/image_analysis/image_stack_profile.rs b/src/tools/image_analysis/image_stack_profile.rs
index c906011d9..ac1ab74c3 100644
--- a/src/tools/image_analysis/image_stack_profile.rs
+++ b/src/tools/image_analysis/image_stack_profile.rs
@@ -150,7 +150,7 @@ impl WhiteboxTool for ImageStackProfile {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/image_analysis/integral_image.rs b/src/tools/image_analysis/integral_image.rs
index c4fabbdee..904c9c32a 100644
--- a/src/tools/image_analysis/integral_image.rs
+++ b/src/tools/image_analysis/integral_image.rs
@@ -126,7 +126,7 @@ impl WhiteboxTool for IntegralImage {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/image_analysis/k_means_clustering.rs b/src/tools/image_analysis/k_means_clustering.rs
index 4da8c5345..7c7dced18 100644
--- a/src/tools/image_analysis/k_means_clustering.rs
+++ b/src/tools/image_analysis/k_means_clustering.rs
@@ -205,7 +205,7 @@ impl WhiteboxTool for KMeansClustering {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/image_analysis/k_nearest_mean_filter.rs b/src/tools/image_analysis/k_nearest_mean_filter.rs
index 6b47df60c..0475aaad9 100644
--- a/src/tools/image_analysis/k_nearest_mean_filter.rs
+++ b/src/tools/image_analysis/k_nearest_mean_filter.rs
@@ -168,7 +168,7 @@ impl WhiteboxTool for KNearestMeanFilter {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/image_analysis/laplacian_filter.rs b/src/tools/image_analysis/laplacian_filter.rs
index 442fda145..cde223d9b 100644
--- a/src/tools/image_analysis/laplacian_filter.rs
+++ b/src/tools/image_analysis/laplacian_filter.rs
@@ -193,7 +193,7 @@ impl WhiteboxTool for LaplacianFilter {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
diff --git a/src/tools/image_analysis/lee_filter.rs b/src/tools/image_analysis/lee_filter.rs
index cbfa0eb6d..c19662e07 100644
--- a/src/tools/image_analysis/lee_filter.rs
+++ b/src/tools/image_analysis/lee_filter.rs
@@ -165,7 +165,7 @@ impl WhiteboxTool for LeeSigmaFilter {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/image_analysis/line_detection_filter.rs b/src/tools/image_analysis/line_detection_filter.rs
index d1cbbbb70..735b3517c 100644
--- a/src/tools/image_analysis/line_detection_filter.rs
+++ b/src/tools/image_analysis/line_detection_filter.rs
@@ -184,7 +184,7 @@ impl WhiteboxTool for LineDetectionFilter {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
diff --git a/src/tools/image_analysis/line_thin.rs b/src/tools/image_analysis/line_thin.rs
index d082e0ecb..10e53761d 100644
--- a/src/tools/image_analysis/line_thin.rs
+++ b/src/tools/image_analysis/line_thin.rs
@@ -132,7 +132,7 @@ impl WhiteboxTool for LineThinning {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/image_analysis/log_filter.rs b/src/tools/image_analysis/log_filter.rs
index 58fe89494..344ff6f04 100644
--- a/src/tools/image_analysis/log_filter.rs
+++ b/src/tools/image_analysis/log_filter.rs
@@ -152,7 +152,7 @@ impl WhiteboxTool for LaplacianOfGaussianFilter {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/image_analysis/majority_filter.rs b/src/tools/image_analysis/majority_filter.rs
index 982bc4069..79a89aaf6 100644
--- a/src/tools/image_analysis/majority_filter.rs
+++ b/src/tools/image_analysis/majority_filter.rs
@@ -155,7 +155,7 @@ impl WhiteboxTool for MajorityFilter {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/image_analysis/max_filter.rs b/src/tools/image_analysis/max_filter.rs
index bc776f31a..368a094d7 100644
--- a/src/tools/image_analysis/max_filter.rs
+++ b/src/tools/image_analysis/max_filter.rs
@@ -152,7 +152,7 @@ impl WhiteboxTool for MaximumFilter {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/image_analysis/mean_filter.rs b/src/tools/image_analysis/mean_filter.rs
index dba36e7df..61e45c48f 100644
--- a/src/tools/image_analysis/mean_filter.rs
+++ b/src/tools/image_analysis/mean_filter.rs
@@ -164,7 +164,7 @@ impl WhiteboxTool for MeanFilter {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/image_analysis/median_filter.rs b/src/tools/image_analysis/median_filter.rs
index cb427d879..17de606be 100644
--- a/src/tools/image_analysis/median_filter.rs
+++ b/src/tools/image_analysis/median_filter.rs
@@ -173,7 +173,7 @@ impl WhiteboxTool for MedianFilter {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/image_analysis/min_filter.rs b/src/tools/image_analysis/min_filter.rs
index 9fb5f7a47..2b4173843 100644
--- a/src/tools/image_analysis/min_filter.rs
+++ b/src/tools/image_analysis/min_filter.rs
@@ -159,7 +159,7 @@ impl WhiteboxTool for MinimumFilter {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/image_analysis/min_max_contrast_stretch.rs b/src/tools/image_analysis/min_max_contrast_stretch.rs
index 19fa2fe18..0f60efe49 100644
--- a/src/tools/image_analysis/min_max_contrast_stretch.rs
+++ b/src/tools/image_analysis/min_max_contrast_stretch.rs
@@ -176,7 +176,7 @@ impl WhiteboxTool for MinMaxContrastStretch {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/image_analysis/modified_k_means_clustering.rs b/src/tools/image_analysis/modified_k_means_clustering.rs
index 5304dbfbb..0da027943 100644
--- a/src/tools/image_analysis/modified_k_means_clustering.rs
+++ b/src/tools/image_analysis/modified_k_means_clustering.rs
@@ -208,7 +208,7 @@ impl WhiteboxTool for ModifiedKMeansClustering {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/image_analysis/mosaic.rs b/src/tools/image_analysis/mosaic.rs
index 13b0b7e26..6cebe80cf 100644
--- a/src/tools/image_analysis/mosaic.rs
+++ b/src/tools/image_analysis/mosaic.rs
@@ -148,7 +148,7 @@ impl WhiteboxTool for Mosaic {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
@@ -433,7 +433,7 @@ impl WhiteboxTool for Mosaic {
for n in 0..num_neighbours {
row_n = origin_row + shift_y[n];
col_n = origin_col + shift_x[n];
- neighbour[n][0] = inputs[i].get_value(row_n, col_n);;
+ neighbour[n][0] = inputs[i].get_value(row_n, col_n);
dy = row_n as f64 - row_src;
dx = col_n as f64 - col_src;
@@ -518,7 +518,7 @@ impl WhiteboxTool for Mosaic {
for n in 0..num_neighbours {
row_n = origin_row + shift_y[n];
col_n = origin_col + shift_x[n];
- neighbour[n][0] = inputs[i].get_value(row_n, col_n);;
+ neighbour[n][0] = inputs[i].get_value(row_n, col_n);
dy = row_n as f64 - row_src;
dx = col_n as f64 - col_src;
diff --git a/src/tools/image_analysis/mosaic_with_feathering.rs b/src/tools/image_analysis/mosaic_with_feathering.rs
index 9776da0a1..5911ab2f3 100644
--- a/src/tools/image_analysis/mosaic_with_feathering.rs
+++ b/src/tools/image_analysis/mosaic_with_feathering.rs
@@ -177,7 +177,7 @@ impl WhiteboxTool for MosaicWithFeathering {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
@@ -543,7 +543,7 @@ impl WhiteboxTool for MosaicWithFeathering {
for n in 0..num_neighbours {
row_n = origin_row1 + shift_y[n];
col_n = origin_col1 + shift_x[n];
- neighbour[n][0] = input1.get_value(row_n, col_n);;
+ neighbour[n][0] = input1.get_value(row_n, col_n);
dy = row_n as f64 - row_src1;
dx = col_n as f64 - col_src1;
@@ -568,7 +568,7 @@ impl WhiteboxTool for MosaicWithFeathering {
for n in 0..num_neighbours {
row_n = origin_row2 + shift_y[n];
col_n = origin_col2 + shift_x[n];
- neighbour[n][0] = input2.get_value(row_n, col_n);;
+ neighbour[n][0] = input2.get_value(row_n, col_n);
dy = row_n as f64 - row_src2;
dx = col_n as f64 - col_src2;
@@ -609,7 +609,7 @@ impl WhiteboxTool for MosaicWithFeathering {
for n in 0..num_neighbours {
row_n = origin_row1 + shift_y[n];
col_n = origin_col1 + shift_x[n];
- neighbour[n][0] = input1.get_value(row_n, col_n);;
+ neighbour[n][0] = input1.get_value(row_n, col_n);
dy = row_n as f64 - row_src1;
dx = col_n as f64 - col_src1;
@@ -642,7 +642,7 @@ impl WhiteboxTool for MosaicWithFeathering {
for n in 0..num_neighbours {
row_n = origin_row2 + shift_y[n];
col_n = origin_col2 + shift_x[n];
- neighbour[n][0] = input2.get_value(row_n, col_n);;
+ neighbour[n][0] = input2.get_value(row_n, col_n);
dy = row_n as f64 - row_src2;
dx = col_n as f64 - col_src2;
diff --git a/src/tools/image_analysis/normalized_difference_index.rs b/src/tools/image_analysis/normalized_difference_index.rs
index e696f90de..3d644a4a1 100644
--- a/src/tools/image_analysis/normalized_difference_index.rs
+++ b/src/tools/image_analysis/normalized_difference_index.rs
@@ -204,7 +204,7 @@ impl WhiteboxTool for NormalizedDifferenceIndex {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/image_analysis/olympic_filter.rs b/src/tools/image_analysis/olympic_filter.rs
index 2675d0b86..3918ceb5a 100644
--- a/src/tools/image_analysis/olympic_filter.rs
+++ b/src/tools/image_analysis/olympic_filter.rs
@@ -156,7 +156,7 @@ impl WhiteboxTool for OlympicFilter {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/image_analysis/opening.rs b/src/tools/image_analysis/opening.rs
index 667781491..cbc60e29b 100644
--- a/src/tools/image_analysis/opening.rs
+++ b/src/tools/image_analysis/opening.rs
@@ -155,7 +155,7 @@ impl WhiteboxTool for Opening {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/image_analysis/pan_sharpening.rs b/src/tools/image_analysis/pan_sharpening.rs
index 33a9376a4..ed8deef92 100644
--- a/src/tools/image_analysis/pan_sharpening.rs
+++ b/src/tools/image_analysis/pan_sharpening.rs
@@ -195,7 +195,7 @@ impl WhiteboxTool for PanchromaticSharpening {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/image_analysis/percentage_contrast_stretch.rs b/src/tools/image_analysis/percentage_contrast_stretch.rs
index 7b171038e..34aa15049 100644
--- a/src/tools/image_analysis/percentage_contrast_stretch.rs
+++ b/src/tools/image_analysis/percentage_contrast_stretch.rs
@@ -178,7 +178,7 @@ impl WhiteboxTool for PercentageContrastStretch {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/image_analysis/percentile_filter.rs b/src/tools/image_analysis/percentile_filter.rs
index 78380320c..7e08481ee 100644
--- a/src/tools/image_analysis/percentile_filter.rs
+++ b/src/tools/image_analysis/percentile_filter.rs
@@ -178,7 +178,7 @@ impl WhiteboxTool for PercentileFilter {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/image_analysis/prewitt_filter.rs b/src/tools/image_analysis/prewitt_filter.rs
index 54484a9ce..84f0c9be1 100644
--- a/src/tools/image_analysis/prewitt_filter.rs
+++ b/src/tools/image_analysis/prewitt_filter.rs
@@ -160,7 +160,7 @@ impl WhiteboxTool for PrewittFilter {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
diff --git a/src/tools/image_analysis/range_filter.rs b/src/tools/image_analysis/range_filter.rs
index 6c2fe5028..9d7a7e14c 100644
--- a/src/tools/image_analysis/range_filter.rs
+++ b/src/tools/image_analysis/range_filter.rs
@@ -154,7 +154,7 @@ impl WhiteboxTool for RangeFilter {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/image_analysis/remove_spurs.rs b/src/tools/image_analysis/remove_spurs.rs
index 5c7dd238f..314a0c393 100644
--- a/src/tools/image_analysis/remove_spurs.rs
+++ b/src/tools/image_analysis/remove_spurs.rs
@@ -149,7 +149,7 @@ impl WhiteboxTool for RemoveSpurs {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/image_analysis/resample.rs b/src/tools/image_analysis/resample.rs
index 64109f1bf..7316233c8 100644
--- a/src/tools/image_analysis/resample.rs
+++ b/src/tools/image_analysis/resample.rs
@@ -136,7 +136,7 @@ impl WhiteboxTool for Resample {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
@@ -346,7 +346,7 @@ impl WhiteboxTool for Resample {
for n in 0..num_neighbours {
row_n = origin_row + shift_y[n];
col_n = origin_col + shift_x[n];
- neighbour[n][0] = inputs[i].get_value(row_n, col_n);;
+ neighbour[n][0] = inputs[i].get_value(row_n, col_n);
dy = row_n as f64 - row_src;
dx = col_n as f64 - col_src;
@@ -429,7 +429,7 @@ impl WhiteboxTool for Resample {
for n in 0..num_neighbours {
row_n = origin_row + shift_y[n];
col_n = origin_col + shift_x[n];
- neighbour[n][0] = inputs[i].get_value(row_n, col_n);;
+ neighbour[n][0] = inputs[i].get_value(row_n, col_n);
dy = row_n as f64 - row_src;
dx = col_n as f64 - col_src;
diff --git a/src/tools/image_analysis/rgb_to_ihs.rs b/src/tools/image_analysis/rgb_to_ihs.rs
index 2cd431cb6..c4e5dc935 100644
--- a/src/tools/image_analysis/rgb_to_ihs.rs
+++ b/src/tools/image_analysis/rgb_to_ihs.rs
@@ -206,7 +206,7 @@ impl WhiteboxTool for RgbToIhs {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/image_analysis/roberts_filter.rs b/src/tools/image_analysis/roberts_filter.rs
index f70a86502..29879eb48 100644
--- a/src/tools/image_analysis/roberts_filter.rs
+++ b/src/tools/image_analysis/roberts_filter.rs
@@ -160,7 +160,7 @@ impl WhiteboxTool for RobertsCrossFilter {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
diff --git a/src/tools/image_analysis/scharr_filter.rs b/src/tools/image_analysis/scharr_filter.rs
index 589b289ae..70704bbdb 100644
--- a/src/tools/image_analysis/scharr_filter.rs
+++ b/src/tools/image_analysis/scharr_filter.rs
@@ -161,7 +161,7 @@ impl WhiteboxTool for ScharrFilter {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
diff --git a/src/tools/image_analysis/sigmoidal_contrast_stretch.rs b/src/tools/image_analysis/sigmoidal_contrast_stretch.rs
index adad34678..3c75f2675 100644
--- a/src/tools/image_analysis/sigmoidal_contrast_stretch.rs
+++ b/src/tools/image_analysis/sigmoidal_contrast_stretch.rs
@@ -178,7 +178,7 @@ impl WhiteboxTool for SigmoidalContrastStretch {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/image_analysis/sobel_filter.rs b/src/tools/image_analysis/sobel_filter.rs
index 483fa4da2..b59b2441e 100644
--- a/src/tools/image_analysis/sobel_filter.rs
+++ b/src/tools/image_analysis/sobel_filter.rs
@@ -168,7 +168,7 @@ impl WhiteboxTool for SobelFilter {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
diff --git a/src/tools/image_analysis/split_colour_composite.rs b/src/tools/image_analysis/split_colour_composite.rs
index 782fcced8..bfe011cf6 100644
--- a/src/tools/image_analysis/split_colour_composite.rs
+++ b/src/tools/image_analysis/split_colour_composite.rs
@@ -157,7 +157,7 @@ impl WhiteboxTool for SplitColourComposite {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/image_analysis/stdev_contrast_stretch.rs b/src/tools/image_analysis/stdev_contrast_stretch.rs
index 3fbecedd1..a1b77f023 100644
--- a/src/tools/image_analysis/stdev_contrast_stretch.rs
+++ b/src/tools/image_analysis/stdev_contrast_stretch.rs
@@ -163,7 +163,7 @@ impl WhiteboxTool for StandardDeviationContrastStretch {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/image_analysis/stdev_filter.rs b/src/tools/image_analysis/stdev_filter.rs
index ce7e55fd3..676d8b0b4 100644
--- a/src/tools/image_analysis/stdev_filter.rs
+++ b/src/tools/image_analysis/stdev_filter.rs
@@ -154,7 +154,7 @@ impl WhiteboxTool for StandardDeviationFilter {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/image_analysis/thicken_line.rs b/src/tools/image_analysis/thicken_line.rs
index e08f67f34..b81a1062f 100644
--- a/src/tools/image_analysis/thicken_line.rs
+++ b/src/tools/image_analysis/thicken_line.rs
@@ -140,7 +140,7 @@ impl WhiteboxTool for ThickenRasterLine {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/image_analysis/tophat.rs b/src/tools/image_analysis/tophat.rs
index 119e4c486..caddc7b46 100644
--- a/src/tools/image_analysis/tophat.rs
+++ b/src/tools/image_analysis/tophat.rs
@@ -172,7 +172,7 @@ impl WhiteboxTool for TophatTransform {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/image_analysis/total_filter.rs b/src/tools/image_analysis/total_filter.rs
index d89824200..1881674cb 100644
--- a/src/tools/image_analysis/total_filter.rs
+++ b/src/tools/image_analysis/total_filter.rs
@@ -153,7 +153,7 @@ impl WhiteboxTool for TotalFilter {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/image_analysis/unsharp_masking.rs b/src/tools/image_analysis/unsharp_masking.rs
index 8faf74d9c..af276ce59 100644
--- a/src/tools/image_analysis/unsharp_masking.rs
+++ b/src/tools/image_analysis/unsharp_masking.rs
@@ -168,7 +168,7 @@ impl WhiteboxTool for UnsharpMasking {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/image_analysis/user_defined_weights_filter.rs b/src/tools/image_analysis/user_defined_weights_filter.rs
index cef2a4bb8..30a40b3ad 100644
--- a/src/tools/image_analysis/user_defined_weights_filter.rs
+++ b/src/tools/image_analysis/user_defined_weights_filter.rs
@@ -161,7 +161,7 @@ impl WhiteboxTool for UserDefinedWeightsFilter {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/image_analysis/write_func_memory_insertion.rs b/src/tools/image_analysis/write_func_memory_insertion.rs
index aa9b12ef3..24d03c8d4 100644
--- a/src/tools/image_analysis/write_func_memory_insertion.rs
+++ b/src/tools/image_analysis/write_func_memory_insertion.rs
@@ -165,7 +165,7 @@ impl WhiteboxTool for WriteFunctionMemoryInsertion {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/lidar_analysis/ascii_to_las.rs b/src/tools/lidar_analysis/ascii_to_las.rs
index 07e510473..b597157b3 100644
--- a/src/tools/lidar_analysis/ascii_to_las.rs
+++ b/src/tools/lidar_analysis/ascii_to_las.rs
@@ -172,7 +172,7 @@ impl WhiteboxTool for AsciiToLas {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/lidar_analysis/block_maximum.rs b/src/tools/lidar_analysis/block_maximum.rs
index a231104e1..796cd8719 100644
--- a/src/tools/lidar_analysis/block_maximum.rs
+++ b/src/tools/lidar_analysis/block_maximum.rs
@@ -145,7 +145,7 @@ impl WhiteboxTool for LidarBlockMaximum {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/lidar_analysis/block_minimum.rs b/src/tools/lidar_analysis/block_minimum.rs
index 4bd49dec3..b3cd7af72 100644
--- a/src/tools/lidar_analysis/block_minimum.rs
+++ b/src/tools/lidar_analysis/block_minimum.rs
@@ -145,7 +145,7 @@ impl WhiteboxTool for LidarBlockMinimum {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/lidar_analysis/classify_buildings.rs b/src/tools/lidar_analysis/classify_buildings.rs
new file mode 100644
index 000000000..e832964e7
--- /dev/null
+++ b/src/tools/lidar_analysis/classify_buildings.rs
@@ -0,0 +1,500 @@
+/*
+This tool is part of the WhiteboxTools geospatial analysis library.
+Authors: Dr. John Lindsay
+Created: 17/11/2019
+Last Modified: 17/11/2019
+License: MIT
+*/
+
+use crate::algorithms;
+use crate::lidar::*;
+use crate::structures::{BoundingBox, Point2D};
+use crate::tools::*;
+use crate::vector::{ShapeType, Shapefile};
+use std::env;
+use std::io::{Error, ErrorKind};
+use std::path;
+use num_cpus;
+use std::sync::{Arc, mpsc};
+use std::thread;
+
+/// This tool can be used to assign the building class (classification value 6) to all points within an
+/// input LiDAR point cloud (`--input`) that are contained within the polygons of an input buildings
+/// footprint vector (`--buildings`). The tool performs a simple point-in-polygon operation to determine
+/// membership. The two inputs (i.e. the LAS file and vector) must share the same map projection. Furthermore,
+/// any error in the definition of the building footprints will result in misclassified points in the output
+/// LAS file (`--output`). In particular, if the footprints extend slightly beyond the actual building,
+/// ground points situated adjacent to the building will be incorrectly classified. Thus, care must be
+/// taken in digitizing building footprint polygons. Furthermore, where there are tall trees that overlap
+/// significantly with the building footprint, these vegetation points will also be incorrectly assigned the
+/// building class value.
+///
+/// # See Also
+/// `FilterLidarClasses`, `LidarGroundPointFilter`, `ClipLidarToPolygon`
+pub struct ClassifyBuildingsInLidar {
+ name: String,
+ description: String,
+ toolbox: String,
+ parameters: Vec,
+ example_usage: String,
+}
+
+impl ClassifyBuildingsInLidar {
+ /// public constructor
+ pub fn new() -> ClassifyBuildingsInLidar {
+ let name = "ClassifyBuildingsInLidar".to_string();
+ let toolbox = "LiDAR Tools".to_string();
+ let description = "Reclassifies a LiDAR points that lie within vector building footprints.".to_string();
+
+ let mut parameters = vec![];
+ parameters.push(ToolParameter {
+ name: "Input File".to_owned(),
+ flags: vec!["-i".to_owned(), "--input".to_owned()],
+ description: "Input LiDAR file.".to_owned(),
+ parameter_type: ParameterType::ExistingFile(ParameterFileType::Lidar),
+ default_value: None,
+ optional: false,
+ });
+
+ parameters.push(ToolParameter {
+ name: "Input Building Polygon File".to_owned(),
+ flags: vec!["--buildings".to_owned()],
+ description: "Input vector polygons file.".to_owned(),
+ parameter_type: ParameterType::ExistingFile(ParameterFileType::Vector(
+ VectorGeometryType::Polygon,
+ )),
+ default_value: None,
+ optional: false,
+ });
+
+ parameters.push(ToolParameter {
+ name: "Output File".to_owned(),
+ flags: vec!["-o".to_owned(), "--output".to_owned()],
+ description: "Output LiDAR file.".to_owned(),
+ parameter_type: ParameterType::NewFile(ParameterFileType::Lidar),
+ default_value: None,
+ optional: false,
+ });
+
+ let sep: String = path::MAIN_SEPARATOR.to_string();
+ let p = format!("{}", env::current_dir().unwrap().display());
+ let e = format!("{}", env::current_exe().unwrap().display());
+ let mut short_exe = e
+ .replace(&p, "")
+ .replace(".exe", "")
+ .replace(".", "")
+ .replace(&sep, "");
+ if e.contains(".exe") {
+ short_exe += ".exe";
+ }
+ let usage = format!(">>.*{0} -r={1} -v --wd=\"*path*to*data*\" -i='data.las' --polygons='lakes.shp' -o='output.las'", short_exe, name).replace("*", &sep);
+
+ ClassifyBuildingsInLidar {
+ name: name,
+ description: description,
+ toolbox: toolbox,
+ parameters: parameters,
+ example_usage: usage,
+ }
+ }
+}
+
+impl WhiteboxTool for ClassifyBuildingsInLidar {
+ fn get_source_file(&self) -> String {
+ String::from(file!())
+ }
+
+ fn get_tool_name(&self) -> String {
+ self.name.clone()
+ }
+
+ fn get_tool_description(&self) -> String {
+ self.description.clone()
+ }
+
+ fn get_tool_parameters(&self) -> String {
+ match serde_json::to_string(&self.parameters) {
+ Ok(json_str) => return format!("{{\"parameters\":{}}}", json_str),
+ Err(err) => return format!("{:?}", err),
+ }
+ }
+
+ fn get_example_usage(&self) -> String {
+ self.example_usage.clone()
+ }
+
+ fn get_toolbox(&self) -> String {
+ self.toolbox.clone()
+ }
+
+ fn run<'a>(
+ &self,
+ args: Vec,
+ working_directory: &'a str,
+ verbose: bool,
+ ) -> Result<(), Error> {
+ let mut input_file = String::new();
+ let mut polygons_file = String::new();
+ let mut output_file = String::new();
+
+ if args.len() == 0 {
+ return Err(Error::new(
+ ErrorKind::InvalidInput,
+ "Tool run with no parameters.",
+ ));
+ }
+ for i in 0..args.len() {
+ let mut arg = args[i].replace("\"", "");
+ arg = arg.replace("\'", "");
+ let cmd = arg.split("="); // in case an equals sign was used
+ let vec = cmd.collect::>();
+ let mut keyval = false;
+ if vec.len() > 1 {
+ keyval = true;
+ }
+ let flag_val = vec[0].to_lowercase().replace("--", "-");
+ if flag_val == "-i" || flag_val == "-input" {
+ input_file = if keyval {
+ vec[1].to_string()
+ } else {
+ args[i + 1].to_string()
+ };
+ } else if flag_val == "-building" || flag_val == "-buildings" {
+ polygons_file = if keyval {
+ vec[1].to_string()
+ } else {
+ args[i + 1].to_string()
+ };
+ } else if flag_val == "-o" || flag_val == "-output" {
+ output_file = if keyval {
+ vec[1].to_string()
+ } else {
+ args[i + 1].to_string()
+ };
+ }
+ }
+
+ if verbose {
+ println!("***************{}", "*".repeat(self.get_tool_name().len()));
+ println!("* Welcome to {} *", self.get_tool_name());
+ println!("***************{}", "*".repeat(self.get_tool_name().len()));
+ }
+
+ let sep: String = path::MAIN_SEPARATOR.to_string();
+
+ let mut progress: usize;
+ let mut old_progress: usize = 1;
+
+ if !input_file.contains(&sep) && !input_file.contains("/") {
+ input_file = format!("{}{}", working_directory, input_file);
+ }
+ if !polygons_file.contains(&sep) && !polygons_file.contains("/") {
+ polygons_file = format!("{}{}", working_directory, polygons_file);
+ }
+ if !output_file.contains(&sep) && !output_file.contains("/") {
+ output_file = format!("{}{}", working_directory, output_file);
+ }
+
+ if verbose {
+ println!("Reading data...")
+ };
+ let input = match LasFile::new(&input_file, "r") {
+ Ok(lf) => lf,
+ Err(err) => panic!(format!("Error reading file {}: {}", input_file, err)),
+ };
+
+ let lidar_bb = BoundingBox::new(
+ input.header.min_x,
+ input.header.max_x,
+ input.header.min_y,
+ input.header.max_y,
+ );
+
+ let polygons = Shapefile::read(&polygons_file)?;
+ let num_records = polygons.num_records;
+
+ let start = Instant::now();
+
+ // make sure the input vector file is of polygon type
+ if polygons.header.shape_type.base_shape_type() != ShapeType::Polygon {
+ return Err(Error::new(
+ ErrorKind::InvalidInput,
+ "The input vector data must be of polygon base shape type.",
+ ));
+ }
+
+ // place the bounding boxes of each of the polygons into a vector
+ let mut bb: Vec = Vec::with_capacity(num_records);
+ let mut feature_bb;
+ let mut record_nums = Vec::with_capacity(num_records);
+ for record_num in 0..polygons.num_records {
+ let record = polygons.get_record(record_num);
+ feature_bb = BoundingBox::new(
+ record.x_min,
+ record.x_max,
+ record.y_min,
+ record.y_max,
+ );
+ if feature_bb.overlaps(lidar_bb) {
+ bb.push(feature_bb);
+ record_nums.push(record_num);
+ }
+ }
+
+ if verbose {
+ println!("Performing reclassification...")
+ };
+
+ let n_points = input.header.number_of_points as usize;
+ let num_points: f64 = (input.header.number_of_points - 1) as f64; // used for progress calculation only
+
+ let num_procs = num_cpus::get();
+ let input = Arc::new(input);
+ let polygons = Arc::new(polygons);
+ let record_nums = Arc::new(record_nums);
+ let bb = Arc::new(bb);
+ let (tx, rx) = mpsc::channel();
+ for tid in 0..num_procs {
+ let input = input.clone();
+ let polygons = polygons.clone();
+ let record_nums = record_nums.clone();
+ let bb = bb.clone();
+ let tx = tx.clone();
+ thread::spawn(move || {
+ let mut p: PointData;
+ let mut record_num: usize;
+ let mut point_in_poly: bool;
+ let mut start_point_in_part: usize;
+ let mut end_point_in_part: usize;
+ for point_num in (0..n_points).filter(|point_num| point_num % num_procs == tid) {
+ p = input.get_point_info(point_num);
+ point_in_poly = false;
+ for r in 0..record_nums.len() {
+ record_num = record_nums[r];
+ if bb[r].is_point_in_box(p.x, p.y) {
+ // it's in the bounding box and worth seeing if it's in the enclosed polygon
+ let record = polygons.get_record(record_num);
+ for part in 0..record.num_parts as usize {
+ if !record.is_hole(part as i32) {
+ // not holes
+ start_point_in_part = record.parts[part] as usize;
+ end_point_in_part = if part < record.num_parts as usize - 1 {
+ record.parts[part + 1] as usize - 1
+ } else {
+ record.num_points as usize - 1
+ };
+
+ if algorithms::point_in_poly(
+ &Point2D { x: p.x, y: p.y },
+ &record.points[start_point_in_part..end_point_in_part + 1],
+ ) {
+ point_in_poly = true;
+ break;
+ }
+ }
+ }
+
+ for part in 0..record.num_parts as usize {
+ if record.is_hole(part as i32) {
+ // holes
+ start_point_in_part = record.parts[part] as usize;
+ end_point_in_part = if part < record.num_parts as usize - 1 {
+ record.parts[part + 1] as usize - 1
+ } else {
+ record.num_points as usize - 1
+ };
+
+ if algorithms::point_in_poly(
+ &Point2D { x: p.x, y: p.y },
+ &record.points[start_point_in_part..end_point_in_part + 1],
+ ) {
+ point_in_poly = false;
+ break;
+ }
+ }
+ }
+ }
+ }
+ match tx.send((point_in_poly, point_num)) {
+ Ok(_) => {}, // do nothing
+ Err(_) => panic!("Error performing clipping operation on point num. {}", point_num),
+ };
+ }
+ });
+ }
+
+ let mut output = LasFile::initialize_using_file(&output_file, &input);
+ output.header.system_id = "EXTRACTION".to_string();
+ let mut num_building_points = 0;
+ for i in 0..n_points {
+ let data = rx.recv().unwrap();
+ if !data.0 {
+ output.add_point_record(input.get_record(data.1));
+ } else {
+ num_building_points += 1;
+ let pr = input.get_record(data.1);
+ let pr2: LidarPointRecord;
+ match pr {
+ LidarPointRecord::PointRecord0 { mut point_data } => {
+ point_data.set_classification(6);
+ pr2 = LidarPointRecord::PointRecord0 {
+ point_data: point_data,
+ };
+ }
+ LidarPointRecord::PointRecord1 {
+ mut point_data,
+ gps_data,
+ } => {
+ point_data.set_classification(6);
+ pr2 = LidarPointRecord::PointRecord1 {
+ point_data: point_data,
+ gps_data: gps_data,
+ };
+ }
+ LidarPointRecord::PointRecord2 {
+ mut point_data,
+ colour_data,
+ } => {
+ point_data.set_classification(6);
+ pr2 = LidarPointRecord::PointRecord2 {
+ point_data: point_data,
+ colour_data: colour_data,
+ };
+ }
+ LidarPointRecord::PointRecord3 {
+ mut point_data,
+ gps_data,
+ colour_data,
+ } => {
+ point_data.set_classification(6);
+ pr2 = LidarPointRecord::PointRecord3 {
+ point_data: point_data,
+ gps_data: gps_data,
+ colour_data: colour_data,
+ };
+ }
+ LidarPointRecord::PointRecord4 {
+ mut point_data,
+ gps_data,
+ wave_packet,
+ } => {
+ point_data.set_classification(6);
+ pr2 = LidarPointRecord::PointRecord4 {
+ point_data: point_data,
+ gps_data: gps_data,
+ wave_packet: wave_packet,
+ };
+ }
+ LidarPointRecord::PointRecord5 {
+ mut point_data,
+ gps_data,
+ colour_data,
+ wave_packet,
+ } => {
+ point_data.set_classification(6);
+ pr2 = LidarPointRecord::PointRecord5 {
+ point_data: point_data,
+ gps_data: gps_data,
+ colour_data: colour_data,
+ wave_packet: wave_packet,
+ };
+ }
+ LidarPointRecord::PointRecord6 {
+ mut point_data,
+ gps_data,
+ } => {
+ point_data.set_classification(6);
+ pr2 = LidarPointRecord::PointRecord6 {
+ point_data: point_data,
+ gps_data: gps_data,
+ };
+ }
+ LidarPointRecord::PointRecord7 {
+ mut point_data,
+ gps_data,
+ colour_data,
+ } => {
+ point_data.set_classification(6);
+ pr2 = LidarPointRecord::PointRecord7 {
+ point_data: point_data,
+ gps_data: gps_data,
+ colour_data: colour_data,
+ };
+ }
+ LidarPointRecord::PointRecord8 {
+ mut point_data,
+ gps_data,
+ colour_data,
+ } => {
+ point_data.set_classification(6);
+ pr2 = LidarPointRecord::PointRecord8 {
+ point_data: point_data,
+ gps_data: gps_data,
+ colour_data: colour_data,
+ };
+ }
+ LidarPointRecord::PointRecord9 {
+ mut point_data,
+ gps_data,
+ wave_packet,
+ } => {
+ point_data.set_classification(6);
+ pr2 = LidarPointRecord::PointRecord9 {
+ point_data: point_data,
+ gps_data: gps_data,
+ wave_packet: wave_packet,
+ };
+ }
+ LidarPointRecord::PointRecord10 {
+ mut point_data,
+ gps_data,
+ colour_data,
+ wave_packet,
+ } => {
+ point_data.set_classification(6);
+ pr2 = LidarPointRecord::PointRecord10 {
+ point_data: point_data,
+ gps_data: gps_data,
+ colour_data: colour_data,
+ wave_packet: wave_packet,
+ };
+ }
+ }
+ output.add_point_record(pr2);
+ }
+ if verbose {
+ progress = (100.0_f64 * i as f64 / num_points) as usize;
+ if progress != old_progress {
+ println!("Progress: {}%", progress);
+ old_progress = progress;
+ }
+ }
+ }
+
+ println!("Number of building points classified: {}", num_building_points);
+
+ let elapsed_time = get_formatted_elapsed_time(start);
+
+ if verbose {
+ println!("Writing output LAS file...");
+ }
+ if output.header.number_of_points > 0 {
+ let _ = match output.write() {
+ Ok(_) => if verbose { println!("Complete!") },
+ Err(e) => println!("error while writing: {:?}", e),
+ };
+ } else {
+ if verbose {
+ println!("Warning: the file {} does not appear to contain any points within the clip polygon. No output file has been created.", output.get_short_filename());
+ }
+ }
+ if verbose {
+ println!(
+ "{}",
+ &format!("Elapsed Time (excluding I/O): {}", elapsed_time)
+ );
+ }
+
+ Ok(())
+ }
+}
diff --git a/src/tools/lidar_analysis/classify_overlap_points.rs b/src/tools/lidar_analysis/classify_overlap_points.rs
index b6347925f..cc767a59b 100644
--- a/src/tools/lidar_analysis/classify_overlap_points.rs
+++ b/src/tools/lidar_analysis/classify_overlap_points.rs
@@ -158,7 +158,7 @@ impl WhiteboxTool for ClassifyOverlapPoints {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/lidar_analysis/clip_lidar_to_polygon.rs b/src/tools/lidar_analysis/clip_lidar_to_polygon.rs
index c49c36413..6c222759d 100644
--- a/src/tools/lidar_analysis/clip_lidar_to_polygon.rs
+++ b/src/tools/lidar_analysis/clip_lidar_to_polygon.rs
@@ -138,7 +138,7 @@ impl WhiteboxTool for ClipLidarToPolygon {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/lidar_analysis/erase_polygon_from_lidar.rs b/src/tools/lidar_analysis/erase_polygon_from_lidar.rs
index 8679485cb..267653477 100644
--- a/src/tools/lidar_analysis/erase_polygon_from_lidar.rs
+++ b/src/tools/lidar_analysis/erase_polygon_from_lidar.rs
@@ -136,7 +136,7 @@ impl WhiteboxTool for ErasePolygonFromLidar {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/lidar_analysis/filter_lidar_classes.rs b/src/tools/lidar_analysis/filter_lidar_classes.rs
index 68e1da6f9..596dd36f8 100644
--- a/src/tools/lidar_analysis/filter_lidar_classes.rs
+++ b/src/tools/lidar_analysis/filter_lidar_classes.rs
@@ -164,7 +164,7 @@ impl WhiteboxTool for FilterLidarClasses {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/lidar_analysis/filter_lidar_scan_angles.rs b/src/tools/lidar_analysis/filter_lidar_scan_angles.rs
index 52737e421..f9c225587 100644
--- a/src/tools/lidar_analysis/filter_lidar_scan_angles.rs
+++ b/src/tools/lidar_analysis/filter_lidar_scan_angles.rs
@@ -132,7 +132,7 @@ impl WhiteboxTool for FilterLidarScanAngles {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/lidar_analysis/find_flightline_edge_points.rs b/src/tools/lidar_analysis/find_flightline_edge_points.rs
index c8765871c..b1c17d070 100644
--- a/src/tools/lidar_analysis/find_flightline_edge_points.rs
+++ b/src/tools/lidar_analysis/find_flightline_edge_points.rs
@@ -122,7 +122,7 @@ impl WhiteboxTool for FindFlightlineEdgePoints {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/lidar_analysis/flightline_overlap.rs b/src/tools/lidar_analysis/flightline_overlap.rs
index 2422f063f..e7a7ca604 100644
--- a/src/tools/lidar_analysis/flightline_overlap.rs
+++ b/src/tools/lidar_analysis/flightline_overlap.rs
@@ -135,7 +135,7 @@ impl WhiteboxTool for FlightlineOverlap {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/lidar_analysis/las_to_ascii.rs b/src/tools/lidar_analysis/las_to_ascii.rs
index 522d94292..762a3cea9 100644
--- a/src/tools/lidar_analysis/las_to_ascii.rs
+++ b/src/tools/lidar_analysis/las_to_ascii.rs
@@ -127,7 +127,7 @@ impl WhiteboxTool for LasToAscii {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/lidar_analysis/las_to_multipoint_shapefile.rs b/src/tools/lidar_analysis/las_to_multipoint_shapefile.rs
index 95efb876d..6e8361fb6 100644
--- a/src/tools/lidar_analysis/las_to_multipoint_shapefile.rs
+++ b/src/tools/lidar_analysis/las_to_multipoint_shapefile.rs
@@ -131,7 +131,7 @@ impl WhiteboxTool for LasToMultipointShapefile {
if args.len() == 0 && working_directory.is_empty() {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/lidar_analysis/las_to_shapefile.rs b/src/tools/lidar_analysis/las_to_shapefile.rs
index a2571221d..2affb0e60 100644
--- a/src/tools/lidar_analysis/las_to_shapefile.rs
+++ b/src/tools/lidar_analysis/las_to_shapefile.rs
@@ -136,7 +136,7 @@ impl WhiteboxTool for LasToShapefile {
if args.len() == 0 && working_directory.is_empty() {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/lidar_analysis/lidar_classify_subset.rs b/src/tools/lidar_analysis/lidar_classify_subset.rs
index 453c043e2..fb4cd24e3 100644
--- a/src/tools/lidar_analysis/lidar_classify_subset.rs
+++ b/src/tools/lidar_analysis/lidar_classify_subset.rs
@@ -189,7 +189,7 @@ impl WhiteboxTool for LidarClassifySubset {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
@@ -283,7 +283,7 @@ impl WhiteboxTool for LidarClassifySubset {
let mut old_progress = 1usize;
let capacity_per_node = 128;
- let mut kdtree = KdTree::new_with_capacity(3, capacity_per_node);
+ let mut kdtree = KdTree::with_capacity(3, capacity_per_node);
println!("Creating tree...");
for i in 0..subset_lidar.header.number_of_points as usize {
let p: PointData = subset_lidar.get_point_info(i);
diff --git a/src/tools/lidar_analysis/lidar_colourize.rs b/src/tools/lidar_analysis/lidar_colourize.rs
index f587fa1b2..087933acc 100644
--- a/src/tools/lidar_analysis/lidar_colourize.rs
+++ b/src/tools/lidar_analysis/lidar_colourize.rs
@@ -1,7 +1,7 @@
/*
This tool is part of the WhiteboxTools geospatial analysis library.
Authors: Dr. John Lindsay
-Created: February 18, 2018
+Created: 18/02/2018
Last Modified: 12/10/2018
License: MIT
*/
@@ -18,6 +18,15 @@ use std::sync::mpsc;
use std::sync::Arc;
use std::thread;
+/// This tool can be used to add red-green-blue (RGB) colour values to the points contained within an
+/// input LAS file (`--in_lidar`), based on the pixel values of an input colour image (`--in_image`). Ideally,
+/// the image has been acquired at the same time as the LiDAR point cloud. If this is not the case, one may
+/// expect that transient objects (e.g. cars) in both input data sets will be incorrectly coloured. The
+/// input image should overlap in extent with the LiDAR data set. You may use the `LidarTileFootprint` tool
+/// to determine the spatial extent of the LAS file.
+///
+/// # See Also
+/// `LidarTileFootprint`
pub struct LidarColourize {
name: String,
description: String,
@@ -135,7 +144,7 @@ impl WhiteboxTool for LidarColourize {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/lidar_analysis/lidar_construct_vector_tin.rs b/src/tools/lidar_analysis/lidar_construct_vector_tin.rs
index 8bdc65a3d..322f2dd6a 100644
--- a/src/tools/lidar_analysis/lidar_construct_vector_tin.rs
+++ b/src/tools/lidar_analysis/lidar_construct_vector_tin.rs
@@ -184,7 +184,7 @@ impl WhiteboxTool for LidarConstructVectorTIN {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/lidar_analysis/lidar_elevation_slice.rs b/src/tools/lidar_analysis/lidar_elevation_slice.rs
index 67c796339..2e6809418 100644
--- a/src/tools/lidar_analysis/lidar_elevation_slice.rs
+++ b/src/tools/lidar_analysis/lidar_elevation_slice.rs
@@ -184,7 +184,7 @@ impl WhiteboxTool for LidarElevationSlice {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/lidar_analysis/lidar_ground_point_filter.rs b/src/tools/lidar_analysis/lidar_ground_point_filter.rs
index ef1256e79..fadab5046 100644
--- a/src/tools/lidar_analysis/lidar_ground_point_filter.rs
+++ b/src/tools/lidar_analysis/lidar_ground_point_filter.rs
@@ -226,7 +226,7 @@ impl WhiteboxTool for LidarGroundPointFilter {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/lidar_analysis/lidar_hex_bin.rs b/src/tools/lidar_analysis/lidar_hex_bin.rs
index e12934526..eb7e2057f 100644
--- a/src/tools/lidar_analysis/lidar_hex_bin.rs
+++ b/src/tools/lidar_analysis/lidar_hex_bin.rs
@@ -172,7 +172,7 @@ impl WhiteboxTool for LidarHexBinning {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/lidar_analysis/lidar_hillshade.rs b/src/tools/lidar_analysis/lidar_hillshade.rs
index 21e98ef84..fc9acc01e 100644
--- a/src/tools/lidar_analysis/lidar_hillshade.rs
+++ b/src/tools/lidar_analysis/lidar_hillshade.rs
@@ -156,7 +156,7 @@ impl WhiteboxTool for LidarHillshade {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/lidar_analysis/lidar_histogram.rs b/src/tools/lidar_analysis/lidar_histogram.rs
index 732f539bb..4e158c23c 100644
--- a/src/tools/lidar_analysis/lidar_histogram.rs
+++ b/src/tools/lidar_analysis/lidar_histogram.rs
@@ -163,7 +163,7 @@ impl WhiteboxTool for LidarHistogram {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/lidar_analysis/lidar_idw_interpolation.rs b/src/tools/lidar_analysis/lidar_idw_interpolation.rs
index c3268b4b5..99ba7739d 100644
--- a/src/tools/lidar_analysis/lidar_idw_interpolation.rs
+++ b/src/tools/lidar_analysis/lidar_idw_interpolation.rs
@@ -8,7 +8,7 @@ License: MIT
NOTES:
1. This tool is designed to work either by specifying a single input and output file or
a working directory containing multiple input LAS files.
-2. Need to add the ability to exclude points based on max scan angle divation.
+2. Need to add the ability to exclude points based on max scan angle deviation.
*/
use crate::lidar::*;
@@ -232,7 +232,7 @@ impl WhiteboxTool for LidarIdwInterpolation {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/lidar_analysis/lidar_info.rs b/src/tools/lidar_analysis/lidar_info.rs
index cd1f04951..2adae3845 100644
--- a/src/tools/lidar_analysis/lidar_info.rs
+++ b/src/tools/lidar_analysis/lidar_info.rs
@@ -62,7 +62,7 @@ impl LidarInfo {
"Flag indicating whether or not to print the variable length records (VLRs)."
.to_owned(),
parameter_type: ParameterType::Boolean,
- default_value: None,
+ default_value: Some("true".to_string()),
optional: true,
});
@@ -71,7 +71,7 @@ impl LidarInfo {
flags: vec!["--geokeys".to_owned()],
description: "Flag indicating whether or not to print the geokeys.".to_owned(),
parameter_type: ParameterType::Boolean,
- default_value: None,
+ default_value: Some("true".to_string()),
optional: true,
});
@@ -152,7 +152,7 @@ impl WhiteboxTool for LidarInfo {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/lidar_analysis/lidar_join.rs b/src/tools/lidar_analysis/lidar_join.rs
index f9d7276a7..e0fb9869a 100644
--- a/src/tools/lidar_analysis/lidar_join.rs
+++ b/src/tools/lidar_analysis/lidar_join.rs
@@ -124,7 +124,7 @@ impl WhiteboxTool for LidarJoin {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/lidar_analysis/lidar_kappa.rs b/src/tools/lidar_analysis/lidar_kappa.rs
index 711083da5..f2278cf4b 100644
--- a/src/tools/lidar_analysis/lidar_kappa.rs
+++ b/src/tools/lidar_analysis/lidar_kappa.rs
@@ -167,7 +167,7 @@ impl WhiteboxTool for LidarKappaIndex {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/lidar_analysis/lidar_nn_gridding.rs b/src/tools/lidar_analysis/lidar_nn_gridding.rs
index 15a3dd5fe..9ecb78982 100644
--- a/src/tools/lidar_analysis/lidar_nn_gridding.rs
+++ b/src/tools/lidar_analysis/lidar_nn_gridding.rs
@@ -221,7 +221,7 @@ impl WhiteboxTool for LidarNearestNeighbourGridding {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/lidar_analysis/lidar_outliers.rs b/src/tools/lidar_analysis/lidar_outliers.rs
index 2e311effd..e96172b2c 100644
--- a/src/tools/lidar_analysis/lidar_outliers.rs
+++ b/src/tools/lidar_analysis/lidar_outliers.rs
@@ -169,7 +169,7 @@ impl WhiteboxTool for LidarRemoveOutliers {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/lidar_analysis/lidar_point_density.rs b/src/tools/lidar_analysis/lidar_point_density.rs
index 7fa20744a..c1ecd537c 100644
--- a/src/tools/lidar_analysis/lidar_point_density.rs
+++ b/src/tools/lidar_analysis/lidar_point_density.rs
@@ -209,7 +209,7 @@ impl WhiteboxTool for LidarPointDensity {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/lidar_analysis/lidar_point_stats.rs b/src/tools/lidar_analysis/lidar_point_stats.rs
index 22d5703e2..6c384e13b 100644
--- a/src/tools/lidar_analysis/lidar_point_stats.rs
+++ b/src/tools/lidar_analysis/lidar_point_stats.rs
@@ -229,7 +229,7 @@ impl WhiteboxTool for LidarPointStats {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/lidar_analysis/lidar_radial_basis_function_interpolation.rs b/src/tools/lidar_analysis/lidar_radial_basis_function_interpolation.rs
new file mode 100644
index 000000000..dce4d1352
--- /dev/null
+++ b/src/tools/lidar_analysis/lidar_radial_basis_function_interpolation.rs
@@ -0,0 +1,1023 @@
+/*
+This tool is part of the WhiteboxTools geospatial analysis library.
+Authors: Dr. John Lindsay
+Created: 08/11/2019
+Last Modified: 08/11/2019
+License: MIT
+
+NOTES:
+1. This tool is designed to work either by specifying a single input and output file or
+ a working directory containing multiple input LAS files.
+2. Need to add the ability to exclude points based on max scan angle deviation.
+*/
+
+use crate::lidar::*;
+use crate::raster::*;
+use crate::structures::{Basis, BoundingBox, DistanceMetric, FixedRadiusSearch2D, RadialBasisFunction};
+use crate::tools::*;
+use num_cpus;
+use nalgebra::DVector;
+use std::env;
+use std::f64;
+use std::fs;
+use std::io::{Error, ErrorKind};
+use std::path;
+use std::sync::mpsc;
+use std::sync::{Arc, Mutex};
+use std::thread;
+
+pub struct LidarRfbInterpolation {
+ name: String,
+ description: String,
+ toolbox: String,
+ parameters: Vec,
+ example_usage: String,
+}
+
+impl LidarRfbInterpolation {
+ pub fn new() -> LidarRfbInterpolation {
+ // public constructor
+ let name = "LidarRfbInterpolation".to_string();
+ let toolbox = "LiDAR Tools".to_string();
+ let description = "Interpolates LAS files using a radial basis function (RFB) scheme. When the input/output parameters are not specified, the tool interpolates all LAS files contained within the working directory."
+ .to_string();
+
+ let mut parameters = vec![];
+ parameters.push(ToolParameter {
+ name: "Input File".to_owned(),
+ flags: vec!["-i".to_owned(), "--input".to_owned()],
+ description: "Input LiDAR file (including extension).".to_owned(),
+ parameter_type: ParameterType::ExistingFile(ParameterFileType::Lidar),
+ default_value: None,
+ optional: true,
+ });
+
+ parameters.push(ToolParameter {
+ name: "Output File".to_owned(),
+ flags: vec!["-o".to_owned(), "--output".to_owned()],
+ description: "Output raster file (including extension).".to_owned(),
+ parameter_type: ParameterType::NewFile(ParameterFileType::Raster),
+ default_value: None,
+ optional: true,
+ });
+
+ parameters.push(ToolParameter{
+ name: "Interpolation Parameter".to_owned(),
+ flags: vec!["--parameter".to_owned()],
+ description: "Interpolation parameter; options are 'elevation' (default), 'intensity', 'class', 'return_number', 'number_of_returns', 'scan angle', 'rgb', 'user data'.".to_owned(),
+ parameter_type: ParameterType::OptionList(
+ vec![
+ "elevation".to_owned(),
+ "intensity".to_owned(),
+ "class".to_owned(),
+ "return_number".to_owned(),
+ "number_of_returns".to_owned(),
+ "scan angle".to_owned(),
+ "rgb".to_owned(),
+ "user data".to_owned()
+ ]
+ ),
+ default_value: Some("elevation".to_owned()),
+ optional: true
+ });
+
+ parameters.push(ToolParameter {
+ name: "Point Returns Included".to_owned(),
+ flags: vec!["--returns".to_owned()],
+ description:
+ "Point return types to include; options are 'all' (default), 'last', 'first'."
+ .to_owned(),
+ parameter_type: ParameterType::OptionList(vec![
+ "all".to_owned(),
+ "last".to_owned(),
+ "first".to_owned(),
+ ]),
+ default_value: Some("all".to_owned()),
+ optional: true,
+ });
+
+ parameters.push(ToolParameter {
+ name: "Grid Resolution".to_owned(),
+ flags: vec!["--resolution".to_owned()],
+ description: "Output raster's grid resolution.".to_owned(),
+ parameter_type: ParameterType::Float,
+ default_value: Some("1.0".to_owned()),
+ optional: true,
+ });
+
+ parameters.push(ToolParameter {
+ name: "Search Radius".to_owned(),
+ flags: vec!["--radius".to_owned()],
+ description: "Search Radius.".to_owned(),
+ parameter_type: ParameterType::Float,
+ default_value: Some("2.5".to_owned()),
+ optional: true,
+ });
+
+ parameters.push(ToolParameter{
+ name: "Exclusion Classes (0-18, based on LAS spec; e.g. 3,4,5,6,7)".to_owned(),
+ flags: vec!["--exclude_cls".to_owned()],
+ description: "Optional exclude classes from interpolation; Valid class values range from 0 to 18, based on LAS specifications. Example, --exclude_cls='3,4,5,6,7,18'.".to_owned(),
+ parameter_type: ParameterType::String,
+ default_value: None,
+ optional: true
+ });
+
+ parameters.push(ToolParameter {
+ name: "Minimum Elevation Value (optional)".to_owned(),
+ flags: vec!["--minz".to_owned()],
+ description: "Optional minimum elevation for inclusion in interpolation.".to_owned(),
+ parameter_type: ParameterType::Float,
+ default_value: None,
+ optional: true,
+ });
+
+ parameters.push(ToolParameter {
+ name: "Maximum Elevation Value (optional)".to_owned(),
+ flags: vec!["--maxz".to_owned()],
+ description: "Optional maximum elevation for inclusion in interpolation.".to_owned(),
+ parameter_type: ParameterType::Float,
+ default_value: None,
+ optional: true,
+ });
+
+ parameters.push(ToolParameter{
+ name: "Radial Basis Function Type".to_owned(),
+ flags: vec!["--func_type".to_owned()],
+ description: "Radial basis function type; options are 'ThinPlateSpline' (default), 'PolyHarmonic', 'Gaussian', 'MultiQuadric', 'InverseMultiQuadric'.".to_owned(),
+ parameter_type: ParameterType::OptionList(
+ vec![
+ "ThinPlateSpline".to_owned(),
+ "PolyHarmonic".to_owned(),
+ "Gaussian".to_owned(),
+ "MultiQuadric".to_owned(),
+ "InverseMultiQuadric".to_owned()
+ ]
+ ),
+ default_value: Some("ThinPlateSpline".to_owned()),
+ optional: true
+ });
+
+ parameters.push(ToolParameter{
+ name: "Polynomial Order".to_owned(),
+ flags: vec!["--poly_order".to_owned()],
+ description: "Polynomial order; options are 'none' (default), 'constant', 'affine'.".to_owned(),
+ parameter_type: ParameterType::OptionList(
+ vec![
+ "none".to_owned(),
+ "constant".to_owned(),
+ "affine".to_owned()
+ ]
+ ),
+ default_value: Some("none".to_owned()),
+ optional: true
+ });
+
+ parameters.push(ToolParameter {
+ name: "Weight".to_owned(),
+ flags: vec!["--weight".to_owned()],
+ description: "Weight parameter used in basis function.".to_owned(),
+ parameter_type: ParameterType::Float,
+ default_value: Some("0.1".to_owned()),
+ optional: false,
+ });
+
+ let sep: String = path::MAIN_SEPARATOR.to_string();
+ let p = format!("{}", env::current_dir().unwrap().display());
+ let e = format!("{}", env::current_exe().unwrap().display());
+ let mut short_exe = e
+ .replace(&p, "")
+ .replace(".exe", "")
+ .replace(".", "")
+ .replace(&sep, "");
+ if e.contains(".exe") {
+ short_exe += ".exe";
+ }
+ let usage = format!(">>.*{0} -r={1} -v --wd=\"*path*to*data*\" -i=file.las -o=outfile.tif --resolution=2.0 --radius=5.0\"
+.*{0} -r={1} --wd=\"*path*to*data*\" -i=file.las -o=outfile.tif --resolution=5.0 --weight=2.0 --radius=2.0 --exclude_cls='3,4,5,6,7,18' --palette=light_quant.plt", short_exe, name).replace("*", &sep);
+
+ LidarRfbInterpolation {
+ name: name,
+ description: description,
+ toolbox: toolbox,
+ parameters: parameters,
+ example_usage: usage,
+ }
+ }
+}
+
+impl WhiteboxTool for LidarRfbInterpolation {
+ fn get_source_file(&self) -> String {
+ String::from(file!())
+ }
+
+ fn get_tool_name(&self) -> String {
+ self.name.clone()
+ }
+
+ fn get_tool_description(&self) -> String {
+ self.description.clone()
+ }
+
+ fn get_tool_parameters(&self) -> String {
+ let mut s = String::from("{\"parameters\": [");
+ for i in 0..self.parameters.len() {
+ if i < self.parameters.len() - 1 {
+ s.push_str(&(self.parameters[i].to_string()));
+ s.push_str(",");
+ } else {
+ s.push_str(&(self.parameters[i].to_string()));
+ }
+ }
+ s.push_str("]}");
+ s
+ }
+
+ fn get_example_usage(&self) -> String {
+ self.example_usage.clone()
+ }
+
+ fn get_toolbox(&self) -> String {
+ self.toolbox.clone()
+ }
+
+ fn run<'a>(
+ &self,
+ args: Vec,
+ working_directory: &'a str,
+ verbose: bool,
+ ) -> Result<(), Error> {
+ let mut input_file: String = "".to_string();
+ let mut output_file: String = "".to_string();
+ let mut interp_parameter = "elevation".to_string();
+ // let mut interp_parameter_is_rgb = false;
+ let mut return_type = "all".to_string();
+ let mut grid_res: f64 = 1.0;
+ let mut search_radius = 2.5;
+ let mut include_class_vals = vec![true; 256];
+ let mut palette = "default".to_string();
+ let mut exclude_cls_str = String::new();
+ let mut max_z = f64::INFINITY;
+ let mut min_z = f64::NEG_INFINITY;
+ let mut func_type = String::from("ThinPlateSpline");
+ let mut poly_order = 0usize;
+ let mut weight = 0.1f64;
+
+ // read the arguments
+ if args.len() == 0 {
+ return Err(Error::new(
+ ErrorKind::InvalidInput,
+ "Tool run with no parameters.",
+ ));
+ }
+ for i in 0..args.len() {
+ let mut arg = args[i].replace("\"", "");
+ arg = arg.replace("\'", "");
+ let cmd = arg.split("="); // in case an equals sign was used
+ let vec = cmd.collect::>();
+ let mut keyval = false;
+ if vec.len() > 1 {
+ keyval = true;
+ }
+ let flag_val = vec[0].to_lowercase().replace("--", "-");
+ if flag_val == "-i" || flag_val == "-input" {
+ input_file = if keyval {
+ vec[1].to_string()
+ } else {
+ args[i + 1].to_string()
+ };
+ } else if flag_val == "-o" || flag_val == "-output" {
+ output_file = if keyval {
+ vec[1].to_string()
+ } else {
+ args[i + 1].to_string()
+ };
+ } else if flag_val == "-parameter" {
+ interp_parameter = if keyval {
+ vec[1].to_string().to_lowercase()
+ } else {
+ args[i + 1].to_string().to_lowercase()
+ };
+ // if interp_parameter == "rgb" {
+ // interp_parameter_is_rgb = true;
+ // }
+ } else if flag_val == "-returns" {
+ return_type = if keyval {
+ vec[1].to_string()
+ } else {
+ args[i + 1].to_string()
+ };
+ } else if flag_val == "-resolution" {
+ grid_res = if keyval {
+ vec[1].to_string().parse::().unwrap()
+ } else {
+ args[i + 1].to_string().parse::().unwrap()
+ };
+ } else if flag_val == "-weight" {
+ weight = if keyval {
+ vec[1].to_string().parse::().unwrap()
+ } else {
+ args[i + 1].to_string().parse::().unwrap()
+ };
+ } else if flag_val == "-radius" {
+ search_radius = if keyval {
+ vec[1].to_string().parse::().unwrap()
+ } else {
+ args[i + 1].to_string().parse::().unwrap()
+ };
+ } else if flag_val == "-palette" {
+ palette = if keyval {
+ vec[1].to_string()
+ } else {
+ args[i + 1].to_string()
+ };
+ } else if flag_val == "-exclude_cls" {
+ exclude_cls_str = if keyval {
+ vec[1].to_string()
+ } else {
+ args[i + 1].to_string()
+ };
+ let mut cmd = exclude_cls_str.split(",");
+ let mut vec = cmd.collect::>();
+ if vec.len() == 1 {
+ cmd = exclude_cls_str.split(";");
+ vec = cmd.collect::>();
+ }
+ for value in vec {
+ if !value.trim().is_empty() {
+ let c = value.trim().parse::().unwrap();
+ include_class_vals[c] = false;
+ }
+ }
+ } else if flag_val == "-minz" {
+ min_z = if keyval {
+ vec[1].to_string().parse::().unwrap()
+ } else {
+ args[i + 1].to_string().parse::().unwrap()
+ };
+ } else if flag_val == "-maxz" {
+ max_z = if keyval {
+ vec[1].to_string().parse::().unwrap()
+ } else {
+ args[i + 1].to_string().parse::().unwrap()
+ };
+ } else if flag_val == "-func_type" {
+ func_type = if keyval {
+ vec[1].to_string().to_lowercase()
+ } else {
+ args[i + 1].to_string().to_lowercase()
+ };
+ } else if flag_val == "-poly_order" {
+ let s = if keyval {
+ vec[1].to_string().to_lowercase()
+ } else {
+ args[i + 1].to_string().to_lowercase()
+ };
+ poly_order = if s.contains("none") {
+ 0usize
+ } else if s.contains("const") {
+ 1usize
+ } else {
+ 2usize
+ };
+ } else if flag_val == "-weight" {
+ weight = if keyval {
+ vec[1].to_string().parse::().unwrap()
+ } else {
+ args[i + 1].to_string().parse::().unwrap()
+ };
+ }
+ }
+
+ let basis_func = if func_type.contains("thin") {
+ Basis::ThinPlateSpine(weight)
+ } else if func_type.contains("PolyHarmonic") {
+ Basis::PolyHarmonic(weight as i32)
+ } else if func_type.contains("Gaussian") {
+ Basis::Gaussian(weight)
+ } else if func_type.contains("MultiQuadric") {
+ Basis::MultiQuadric(weight)
+ } else { //if func_type.contains("InverseMultiQuadric") {
+ Basis::InverseMultiQuadric(weight)
+ };
+
+ let (all_returns, late_returns, early_returns): (bool, bool, bool);
+ if return_type.contains("last") {
+ all_returns = false;
+ late_returns = true;
+ early_returns = false;
+ } else if return_type.contains("first") {
+ all_returns = false;
+ late_returns = false;
+ early_returns = true;
+ } else {
+ // all
+ all_returns = true;
+ late_returns = false;
+ early_returns = false;
+ }
+
+ if verbose {
+ println!("***************{}", "*".repeat(self.get_tool_name().len()));
+ println!("* Welcome to {} *", self.get_tool_name());
+ println!("***************{}", "*".repeat(self.get_tool_name().len()));
+ }
+
+ let start = Instant::now();
+
+ let mut inputs = vec![];
+ let mut outputs = vec![];
+ if input_file.is_empty() {
+ if working_directory.is_empty() {
+ return Err(Error::new(ErrorKind::InvalidInput,
+ "This tool must be run by specifying either an individual input file or a working directory."));
+ }
+ if std::path::Path::new(&working_directory).is_dir() {
+ for entry in fs::read_dir(working_directory.clone())? {
+ let s = entry?
+ .path()
+ .into_os_string()
+ .to_str()
+ .expect("Error reading path string")
+ .to_string();
+ if s.to_lowercase().ends_with(".las") {
+ inputs.push(s);
+ outputs.push(
+ inputs[inputs.len() - 1]
+ .replace(".las", ".tif")
+ .replace(".LAS", ".tif"),
+ )
+ } else if s.to_lowercase().ends_with(".zip") {
+ inputs.push(s);
+ outputs.push(
+ inputs[inputs.len() - 1]
+ .replace(".zip", ".tif")
+ .replace(".ZIP", ".tif"),
+ )
+ }
+ }
+ } else {
+ return Err(Error::new(
+ ErrorKind::InvalidInput,
+ format!("The input directory ({}) is incorrect.", working_directory),
+ ));
+ }
+ } else {
+ if !input_file.contains(path::MAIN_SEPARATOR) && !input_file.contains("/") {
+ input_file = format!("{}{}", working_directory, input_file);
+ }
+ inputs.push(input_file.clone());
+ if output_file.is_empty() {
+ output_file = input_file
+ .clone()
+ .replace(".las", ".tif")
+ .replace(".LAS", ".tif");
+ }
+ if !output_file.contains(path::MAIN_SEPARATOR) && !output_file.contains("/") {
+ output_file = format!("{}{}", working_directory, output_file);
+ }
+ outputs.push(output_file);
+ }
+
+ /*
+ If multiple files are being interpolated, we will need to know their bounding boxes,
+ in order to retrieve points from adjacent tiles. This is so that there are no edge
+ effects.
+ */
+ let mut bounding_boxes = vec![];
+ for in_file in &inputs {
+ let header = LasHeader::read_las_header(&in_file.replace("\"", ""))?;
+ bounding_boxes.push(BoundingBox {
+ min_x: header.min_x,
+ max_x: header.max_x,
+ min_y: header.min_y,
+ max_y: header.max_y,
+ });
+ }
+
+ if verbose {
+ println!("Performing interpolation...");
+ }
+
+ let num_tiles = inputs.len();
+ let tile_list = Arc::new(Mutex::new(0..num_tiles));
+ let inputs = Arc::new(inputs);
+ let outputs = Arc::new(outputs);
+ let bounding_boxes = Arc::new(bounding_boxes);
+ let num_procs2 = num_cpus::get() as isize;
+ let (tx2, rx2) = mpsc::channel();
+ for _ in 0..num_procs2 {
+ let inputs = inputs.clone();
+ let outputs = outputs.clone();
+ let bounding_boxes = bounding_boxes.clone();
+ let tile_list = tile_list.clone();
+ // copy over the string parameters
+ let interp_parameter = interp_parameter.clone();
+ let palette = palette.clone();
+ let return_type = return_type.clone();
+ let tool_name = self.get_tool_name();
+ let exclude_cls_str = exclude_cls_str.clone();
+ let include_class_vals = include_class_vals.clone();
+ let tx2 = tx2.clone();
+ thread::spawn(move || {
+ let mut tile = 0;
+ while tile < num_tiles {
+ // Get the next tile up for interpolation
+ tile = match tile_list.lock().unwrap().next() {
+ Some(val) => val,
+ None => break, // There are no more tiles to interpolate
+ };
+ // for tile in (0..inputs.len()).filter(|t| t % num_procs2 as usize == tid as usize) {
+ let start_run = Instant::now();
+
+ let input_file = inputs[tile].replace("\"", "").clone();
+ let output_file = outputs[tile].replace("\"", "").clone();
+
+ // Expand the bounding box to include the areas of overlap
+ let bb = BoundingBox {
+ min_x: bounding_boxes[tile].min_x - search_radius,
+ max_x: bounding_boxes[tile].max_x + search_radius,
+ min_y: bounding_boxes[tile].min_y - search_radius,
+ max_y: bounding_boxes[tile].max_y + search_radius,
+ };
+ let mut frs: FixedRadiusSearch2D = FixedRadiusSearch2D::new(search_radius, DistanceMetric::Euclidean);
+
+ let mut points = vec![];
+ let mut z_values = vec![];
+
+ if verbose && inputs.len() == 1 {
+ println!("Reading input LAS file...");
+ }
+
+ let mut progress: i32;
+ let mut old_progress: i32 = -1;
+
+ for m in 0..inputs.len() {
+ if bounding_boxes[m].overlaps(bb) {
+ let input =
+ match LasFile::new(&inputs[m].replace("\"", "").clone(), "r") {
+ Ok(lf) => lf,
+ Err(err) => panic!(
+ "Error reading file {}: {}",
+ inputs[m].replace("\"", ""),
+ err
+ ),
+ };
+
+ let n_points = input.header.number_of_points as usize;
+ let num_points: f64 = (input.header.number_of_points - 1) as f64; // used for progress calculation only
+
+ let mut pt = 0;
+ match &interp_parameter as &str {
+ "elevation" | "z" => {
+ for i in 0..n_points {
+ let p: PointData = input[i];
+ if !p.withheld() {
+ if all_returns
+ || (p.is_late_return() & late_returns)
+ || (p.is_early_return() & early_returns)
+ {
+ if include_class_vals[p.classification() as usize] {
+ if bb.is_point_in_box(p.x, p.y)
+ && p.z >= min_z
+ && p.z <= max_z
+ {
+ frs.insert(p.x, p.y, pt);
+ pt += 1;
+ points.push(DVector::from_vec(vec![p.x, p.y]));
+ z_values.push(DVector::from_vec(vec![p.z]));
+ }
+ }
+ }
+ }
+ if verbose && inputs.len() == 1 {
+ progress = (100.0_f64 * i as f64 / num_points) as i32;
+ if progress != old_progress {
+ println!("Binning points: {}%", progress);
+ old_progress = progress;
+ }
+ }
+ }
+ }
+ "intensity" => {
+ for i in 0..n_points {
+ let p: PointData = input[i];
+ if !p.withheld() {
+ if all_returns
+ || (p.is_late_return() & late_returns)
+ || (p.is_early_return() & early_returns)
+ {
+ if include_class_vals[p.classification() as usize] {
+ if bb.is_point_in_box(p.x, p.y)
+ && p.z >= min_z
+ && p.z <= max_z
+ {
+ frs.insert(p.x, p.y, pt);
+ pt += 1;
+ points.push(DVector::from_vec(vec![p.x, p.y]));
+ z_values.push(DVector::from_vec(vec![p.intensity as f64]));
+ }
+ }
+ }
+ }
+ if verbose && inputs.len() == 1 {
+ progress = (100.0_f64 * i as f64 / num_points) as i32;
+ if progress != old_progress {
+ println!("Binning points: {}%", progress);
+ old_progress = progress;
+ }
+ }
+ }
+ }
+ "scan angle" | "scan_angle" => {
+ for i in 0..n_points {
+ let p: PointData = input[i];
+ if !p.withheld() {
+ if all_returns
+ || (p.is_late_return() & late_returns)
+ || (p.is_early_return() & early_returns)
+ {
+ if include_class_vals[p.classification() as usize] {
+ if bb.is_point_in_box(p.x, p.y)
+ && p.z >= min_z
+ && p.z <= max_z
+ {
+ frs.insert(p.x, p.y, pt);
+ pt += 1;
+ points.push(DVector::from_vec(vec![p.x, p.y]));
+ z_values.push(DVector::from_vec(vec![p.scan_angle as f64]));
+ }
+ }
+ }
+ }
+ if verbose && inputs.len() == 1 {
+ progress = (100.0_f64 * i as f64 / num_points) as i32;
+ if progress != old_progress {
+ println!("Binning points: {}%", progress);
+ old_progress = progress;
+ }
+ }
+ }
+ }
+ "class" => {
+ for i in 0..n_points {
+ let p: PointData = input[i];
+ if !p.withheld() {
+ if all_returns
+ || (p.is_late_return() & late_returns)
+ || (p.is_early_return() & early_returns)
+ {
+ if include_class_vals[p.classification() as usize] {
+ if bb.is_point_in_box(p.x, p.y)
+ && p.z >= min_z
+ && p.z <= max_z
+ {
+ frs.insert(p.x, p.y, pt);
+ pt += 1;
+ points.push(DVector::from_vec(vec![p.x, p.y]));
+ z_values.push(DVector::from_vec(vec![p.classification() as f64]));
+ }
+ }
+ }
+ }
+ if verbose && inputs.len() == 1 {
+ progress = (100.0_f64 * i as f64 / num_points) as i32;
+ if progress != old_progress {
+ println!("Binning points: {}%", progress);
+ old_progress = progress;
+ }
+ }
+ }
+ }
+ "return_number" => {
+ for i in 0..n_points {
+ let p: PointData = input[i];
+ if !p.withheld() {
+ if all_returns
+ || (p.is_late_return() & late_returns)
+ || (p.is_early_return() & early_returns)
+ {
+ if include_class_vals[p.classification() as usize] {
+ if bb.is_point_in_box(p.x, p.y)
+ && p.z >= min_z
+ && p.z <= max_z
+ {
+ frs.insert(p.x, p.y, pt);
+ pt += 1;
+ points.push(DVector::from_vec(vec![p.x, p.y]));
+ z_values.push(DVector::from_vec(vec![p.return_number() as f64]));
+ }
+ }
+ }
+ }
+ if verbose && inputs.len() == 1 {
+ progress = (100.0_f64 * i as f64 / num_points) as i32;
+ if progress != old_progress {
+ println!("Reading points: {}%", progress);
+ old_progress = progress;
+ }
+ }
+ }
+ }
+ "number_of_returns" => {
+ for i in 0..n_points {
+ let p: PointData = input[i];
+ if !p.withheld() {
+ if all_returns
+ || (p.is_late_return() & late_returns)
+ || (p.is_early_return() & early_returns)
+ {
+ if include_class_vals[p.classification() as usize] {
+ if bb.is_point_in_box(p.x, p.y)
+ && p.z >= min_z
+ && p.z <= max_z
+ {
+ frs.insert(p.x, p.y, pt);
+ pt += 1;
+ points.push(DVector::from_vec(vec![p.x, p.y]));
+ z_values.push(DVector::from_vec(vec![p.number_of_returns() as f64]));
+ }
+ }
+ }
+ }
+ if verbose && inputs.len() == 1 {
+ progress = (100.0_f64 * i as f64 / num_points) as i32;
+ if progress != old_progress {
+ println!("Reading points: {}%", progress);
+ old_progress = progress;
+ }
+ }
+ }
+ }
+ "rgb" => {
+ if !input.has_rgb() {
+ println!("Error: The input LAS file does not contain RGB colour data. The interpolation will not proceed.");
+ break;
+ }
+ // let mut clr: ColourData;
+ for i in 0..n_points {
+ let p: PointData = input[i];
+ if !p.withheld() {
+ if all_returns
+ || (p.is_late_return() & late_returns)
+ || (p.is_early_return() & early_returns)
+ {
+ if include_class_vals[p.classification() as usize] {
+ if bb.is_point_in_box(p.x, p.y)
+ && p.z >= min_z
+ && p.z <= max_z
+ {
+ // clr = match input.get_rgb(i) {
+ // Ok(value) => { value },
+ // Err(_) => break,
+ // };
+ // ***************************
+ // This needs to be fixed
+ // ***************************
+ // frs.insert(p.x, p.y, ((255u32 << 24) | ((clr.blue as u32) << 16) | ((clr.green as u32) << 8) | (clr.red as u32)) as f64);
+ frs.insert(p.x, p.y, pt);
+ pt += 1;
+ points.push(DVector::from_vec(vec![p.x, p.y]));
+ z_values.push(DVector::from_vec(vec![p.classification() as f64]));
+ }
+ }
+ }
+ }
+ if verbose && inputs.len() == 1 {
+ progress = (100.0_f64 * i as f64 / num_points) as i32;
+ if progress != old_progress {
+ println!("Reading points: {}%", progress);
+ old_progress = progress;
+ }
+ }
+ }
+ }
+ _ => {
+ // user data
+ for i in 0..n_points {
+ let p: PointData = input[i];
+ if !p.withheld() {
+ if all_returns
+ || (p.is_late_return() & late_returns)
+ || (p.is_early_return() & early_returns)
+ {
+ if include_class_vals[p.classification() as usize] {
+ if bb.is_point_in_box(p.x, p.y)
+ && p.z >= min_z
+ && p.z <= max_z
+ {
+ frs.insert(p.x, p.y, pt);
+ pt += 1;
+ points.push(DVector::from_vec(vec![p.x, p.y]));
+ z_values.push(DVector::from_vec(vec![p.user_data as f64]));
+ }
+ }
+ }
+ }
+ if verbose && inputs.len() == 1 {
+ progress = (100.0_f64 * i as f64 / num_points) as i32;
+ if progress != old_progress {
+ println!("Binning points: {}%", progress);
+ old_progress = progress;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ let west: f64 = bounding_boxes[tile].min_x;
+ let north: f64 = bounding_boxes[tile].max_y;
+ let rows: isize =
+ (((north - bounding_boxes[tile].min_y) / grid_res).ceil()) as isize;
+ let columns: isize =
+ (((bounding_boxes[tile].max_x - west) / grid_res).ceil()) as isize;
+ let south: f64 = north - rows as f64 * grid_res;
+ let east = west + columns as f64 * grid_res;
+ let nodata = -32768.0f64;
+
+ let mut configs = RasterConfigs {
+ ..Default::default()
+ };
+ configs.rows = rows as usize;
+ configs.columns = columns as usize;
+ configs.north = north;
+ configs.south = south;
+ configs.east = east;
+ configs.west = west;
+ configs.resolution_x = grid_res;
+ configs.resolution_y = grid_res;
+ configs.nodata = nodata;
+ configs.data_type = DataType::F32;
+ configs.photometric_interp = PhotometricInterpretation::Continuous;
+ configs.palette = palette.clone();
+
+ let mut output = Raster::initialize_using_config(&output_file, &configs);
+ if interp_parameter == "rgb" {
+ output.configs.photometric_interp = PhotometricInterpretation::RGB;
+ output.configs.data_type = DataType::RGBA32;
+ }
+
+ if num_tiles > 1 {
+ let (mut x, mut y): (f64, f64);
+ let mut zn: f64;
+ // let (mut val_red, mut val_green, mut val_blue): (f64, f64, f64);
+ // let (mut red, mut green, mut blue): (f64, f64, f64);
+ for row in 0..rows {
+ for col in 0..columns {
+ x = west + (col as f64 + 0.5) * grid_res;
+ y = north - (row as f64 + 0.5) * grid_res;
+
+ let mut ret = frs.search(x, y);
+ if ret.len() < 6 {
+ ret = frs.knn_search(x, y, 6);
+ }
+ if ret.len() > 0 {
+ let mut centers: Vec> = Vec::with_capacity(ret.len());
+ let mut vals: Vec> = Vec::with_capacity(ret.len());
+ for j in 0..ret.len() {
+ centers.push(points[ret[j].0].clone());
+ vals.push(z_values[ret[j].0].clone());
+ }
+ let rfb = RadialBasisFunction::create(centers, vals, basis_func, poly_order);
+ zn = rfb.eval(DVector::from_vec(vec![x, y]))[0];
+ output.set_value(row, col, zn);
+ } else {
+
+ }
+ }
+ if verbose && inputs.len() == 1 {
+ progress = (100.0_f64 * row as f64 / (rows - 1) as f64) as i32;
+ if progress != old_progress {
+ println!("Progress: {}%", progress);
+ old_progress = progress;
+ }
+ }
+ }
+ } else {
+ // there's only one tile, so use all cores to interpolate this one tile.
+ let points = Arc::new(points);
+ let z_values = Arc::new(z_values);
+ let frs = Arc::new(frs); // wrap FRS in an Arc
+ let num_procs = num_cpus::get() as isize;
+ let (tx, rx) = mpsc::channel();
+ for tid in 0..num_procs {
+ let frs = frs.clone();
+ let tx1 = tx.clone();
+ let points = points.clone();
+ let z_values = z_values.clone();
+ thread::spawn(move || {
+ let (mut x, mut y): (f64, f64);
+ let mut zn: f64;
+
+ // let (mut val_red, mut val_green, mut val_blue): (f64, f64, f64);
+ // let (mut red, mut green, mut blue): (f64, f64, f64);
+ for row in (0..rows).filter(|r| r % num_procs == tid) {
+ // for row in 0..rows {
+ let mut data = vec![nodata; columns as usize];
+ for col in 0..columns {
+ x = west + (col as f64 + 0.5) * grid_res;
+ y = north - (row as f64 + 0.5) * grid_res;
+ let mut ret = frs.search(x, y);
+ if ret.len() < 6 {
+ ret = frs.knn_search(x, y, 6);
+ }
+ if ret.len() > 0 {
+ let mut centers: Vec> = Vec::with_capacity(ret.len());
+ let mut vals: Vec> = Vec::with_capacity(ret.len());
+ for j in 0..ret.len() {
+ centers.push(points[ret[j].0].clone());
+ vals.push(z_values[ret[j].0].clone());
+ }
+ let rfb = RadialBasisFunction::create(centers, vals, basis_func, poly_order);
+ zn = rfb.eval(DVector::from_vec(vec![x, y]))[0];
+ data[col as usize] = zn;
+ }
+ }
+ tx1.send((row, data)).unwrap();
+ }
+ });
+ }
+
+ for row in 0..rows {
+ let data = rx.recv().unwrap();
+ output.set_row_data(data.0, data.1);
+ if verbose {
+ progress = (100.0_f64 * row as f64 / (rows - 1) as f64) as i32;
+ if progress != old_progress {
+ println!("Progress: {}%", progress);
+ old_progress = progress;
+ }
+ }
+ }
+ }
+
+ let elapsed_time_run = get_formatted_elapsed_time(start_run);
+
+ output.add_metadata_entry(format!(
+ "Created by whitebox_tools\' {} tool",
+ tool_name
+ ));
+ output.add_metadata_entry(format!("Input file: {}", input_file));
+ output.add_metadata_entry(format!("Grid resolution: {}", grid_res));
+ output.add_metadata_entry(format!("Search radius: {}", search_radius));
+ output.add_metadata_entry(format!("Weight: {}", weight));
+ output.add_metadata_entry(format!(
+ "Interpolation parameter: {}",
+ interp_parameter
+ ));
+ output.add_metadata_entry(format!("Returns: {}", return_type));
+ output.add_metadata_entry(format!("Excluded classes: {}", exclude_cls_str));
+ output.add_metadata_entry(format!(
+ "Elapsed Time (including I/O): {}",
+ elapsed_time_run
+ ));
+
+ if verbose && inputs.len() == 1 {
+ println!("Saving data...")
+ };
+
+ let _ = output.write().unwrap();
+
+ tx2.send(tile).unwrap();
+ }
+ });
+ }
+
+ let mut progress: i32;
+ let mut old_progress: i32 = -1;
+ for tile in 0..inputs.len() {
+ let tile_completed = rx2.recv().unwrap();
+ if verbose {
+ println!(
+ "Finished interpolating {} ({} of {})",
+ inputs[tile_completed]
+ .replace("\"", "")
+ .replace(working_directory, "")
+ .replace(".las", ""),
+ tile + 1,
+ inputs.len()
+ );
+ }
+ if verbose {
+ progress = (100.0_f64 * tile as f64 / (inputs.len() - 1) as f64) as i32;
+ if progress != old_progress {
+ println!("Progress: {}%", progress);
+ old_progress = progress;
+ }
+ }
+ }
+
+ let elapsed_time = get_formatted_elapsed_time(start);
+
+ if verbose {
+ println!(
+ "{}",
+ &format!("Elapsed Time (including I/O): {}", elapsed_time)
+ );
+ }
+
+ Ok(())
+ }
+}
diff --git a/src/tools/lidar_analysis/lidar_ransac_planes.rs b/src/tools/lidar_analysis/lidar_ransac_planes.rs
index c0ff81d00..9bdf674f4 100644
--- a/src/tools/lidar_analysis/lidar_ransac_planes.rs
+++ b/src/tools/lidar_analysis/lidar_ransac_planes.rs
@@ -201,7 +201,7 @@ impl WhiteboxTool for LidarRansacPlanes {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/lidar_analysis/lidar_segmentation.rs b/src/tools/lidar_analysis/lidar_segmentation.rs
index 2550a3078..7b0a47e8b 100644
--- a/src/tools/lidar_analysis/lidar_segmentation.rs
+++ b/src/tools/lidar_analysis/lidar_segmentation.rs
@@ -189,7 +189,7 @@ impl WhiteboxTool for LidarSegmentation {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/lidar_analysis/lidar_segmentation_based_filter.rs b/src/tools/lidar_analysis/lidar_segmentation_based_filter.rs
index 5f445fba0..00c5b64ee 100644
--- a/src/tools/lidar_analysis/lidar_segmentation_based_filter.rs
+++ b/src/tools/lidar_analysis/lidar_segmentation_based_filter.rs
@@ -168,7 +168,7 @@ impl WhiteboxTool for LidarSegmentationBasedFilter {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/lidar_analysis/lidar_thin.rs b/src/tools/lidar_analysis/lidar_thin.rs
index 27cc53dd8..d2da659a1 100644
--- a/src/tools/lidar_analysis/lidar_thin.rs
+++ b/src/tools/lidar_analysis/lidar_thin.rs
@@ -158,7 +158,7 @@ impl WhiteboxTool for LidarThin {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/lidar_analysis/lidar_thin_high_density.rs b/src/tools/lidar_analysis/lidar_thin_high_density.rs
index 294628907..7ebf9c843 100644
--- a/src/tools/lidar_analysis/lidar_thin_high_density.rs
+++ b/src/tools/lidar_analysis/lidar_thin_high_density.rs
@@ -153,7 +153,7 @@ impl WhiteboxTool for LidarThinHighDensity {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/lidar_analysis/lidar_tile.rs b/src/tools/lidar_analysis/lidar_tile.rs
index 063d31f4d..31e1bae18 100644
--- a/src/tools/lidar_analysis/lidar_tile.rs
+++ b/src/tools/lidar_analysis/lidar_tile.rs
@@ -169,7 +169,7 @@ impl WhiteboxTool for LidarTile {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/lidar_analysis/lidar_tile_footprint.rs b/src/tools/lidar_analysis/lidar_tile_footprint.rs
index 406d6a424..53858a026 100644
--- a/src/tools/lidar_analysis/lidar_tile_footprint.rs
+++ b/src/tools/lidar_analysis/lidar_tile_footprint.rs
@@ -163,7 +163,7 @@ impl WhiteboxTool for LidarTileFootprint {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
@@ -333,14 +333,15 @@ impl WhiteboxTool for LidarTileFootprint {
short_filename,
n_points,
input.header.min_z,
- input.header.max_z
+ input.header.max_z,
+ input.get_wkt()
)).unwrap();
}
Err(err) => {
tx.send((
vec![],
format!("Error reading file {}:\n{}", input_file, err),
- 0, 0f64, 0f64
+ 0, 0f64, 0f64, "".to_string()
))
.unwrap();
}
@@ -364,14 +365,15 @@ impl WhiteboxTool for LidarTileFootprint {
short_filename,
header.get_number_of_points() as usize,
header.min_z,
- header.max_z
+ header.max_z,
+ "".to_string()
)).unwrap();
}
Err(err) => {
tx.send((
vec![],
format!("Error reading file {}:\n{}", input_file, err),
- 0, 0f64, 0f64
+ 0, 0f64, 0f64, "".to_string()
))
.unwrap();
}
@@ -410,6 +412,9 @@ impl WhiteboxTool for LidarTileFootprint {
],
false,
);
+ if !data.5.is_empty() && output.projection.is_empty() {
+ output.projection = data.5.clone();
+ }
} else {
// there was an error, likely reading a LAS file.
println!("{}", data.1);
@@ -426,6 +431,12 @@ impl WhiteboxTool for LidarTileFootprint {
}
}
+ if output.projection.is_empty() {
+ let input_file = inputs[0].replace("\"", "").clone();
+ let mut input = LasFile::new(&input_file, "r")?;
+ output.projection = input.get_wkt().clone();
+ }
+
let data = wkt.lock().unwrap();
if *data != "" && *data != "Unknown EPSG Code" && output.projection.is_empty() {
output.projection = data.to_string();
diff --git a/src/tools/lidar_analysis/lidar_tin_gridding.rs b/src/tools/lidar_analysis/lidar_tin_gridding.rs
index 3edb74d70..7a5817951 100644
--- a/src/tools/lidar_analysis/lidar_tin_gridding.rs
+++ b/src/tools/lidar_analysis/lidar_tin_gridding.rs
@@ -19,7 +19,6 @@ use std::sync::mpsc;
use std::sync::{Arc, Mutex};
use std::{env, f64, fs, path, thread};
-/// Creates a raster grid based on a Delaunay triangular irregular network (TIN) fitted to LiDAR points.
pub struct LidarTINGridding {
name: String,
description: String,
@@ -214,7 +213,7 @@ impl WhiteboxTool for LidarTINGridding {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/lidar_analysis/lidar_tophat_transform.rs b/src/tools/lidar_analysis/lidar_tophat_transform.rs
index 70b909ec0..bf2e67442 100644
--- a/src/tools/lidar_analysis/lidar_tophat_transform.rs
+++ b/src/tools/lidar_analysis/lidar_tophat_transform.rs
@@ -145,7 +145,7 @@ impl WhiteboxTool for LidarTophatTransform {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/lidar_analysis/mod.rs b/src/tools/lidar_analysis/mod.rs
index 6f3ddc8d0..05ed0e227 100644
--- a/src/tools/lidar_analysis/mod.rs
+++ b/src/tools/lidar_analysis/mod.rs
@@ -2,6 +2,7 @@
// mod ascii_to_las;
mod block_maximum;
mod block_minimum;
+mod classify_buildings;
mod classify_overlap_points;
mod clip_lidar_to_polygon;
mod erase_polygon_from_lidar;
@@ -28,6 +29,7 @@ mod lidar_nn_gridding;
mod lidar_outliers;
mod lidar_point_density;
mod lidar_point_stats;
+mod lidar_radial_basis_function_interpolation;
mod lidar_ransac_planes;
mod lidar_segmentation;
mod lidar_segmentation_based_filter;
@@ -45,6 +47,7 @@ mod select_tiles_by_polygon;
// pub use self::ascii_to_las::AsciiToLas;
pub use self::block_maximum::LidarBlockMaximum;
pub use self::block_minimum::LidarBlockMinimum;
+pub use self::classify_buildings::ClassifyBuildingsInLidar;
pub use self::classify_overlap_points::ClassifyOverlapPoints;
pub use self::clip_lidar_to_polygon::ClipLidarToPolygon;
pub use self::erase_polygon_from_lidar::ErasePolygonFromLidar;
@@ -71,6 +74,7 @@ pub use self::lidar_nn_gridding::LidarNearestNeighbourGridding;
pub use self::lidar_outliers::LidarRemoveOutliers;
pub use self::lidar_point_density::LidarPointDensity;
pub use self::lidar_point_stats::LidarPointStats;
+pub use self::lidar_radial_basis_function_interpolation::LidarRfbInterpolation;
pub use self::lidar_ransac_planes::LidarRansacPlanes;
pub use self::lidar_segmentation::LidarSegmentation;
pub use self::lidar_segmentation_based_filter::LidarSegmentationBasedFilter;
diff --git a/src/tools/lidar_analysis/normal_vectors.rs b/src/tools/lidar_analysis/normal_vectors.rs
index 61570d0d8..1da763f1e 100644
--- a/src/tools/lidar_analysis/normal_vectors.rs
+++ b/src/tools/lidar_analysis/normal_vectors.rs
@@ -136,7 +136,7 @@ impl WhiteboxTool for NormalVectors {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/lidar_analysis/remove_duplicates.rs b/src/tools/lidar_analysis/remove_duplicates.rs
index c6000c3d0..7711d7c42 100644
--- a/src/tools/lidar_analysis/remove_duplicates.rs
+++ b/src/tools/lidar_analysis/remove_duplicates.rs
@@ -143,7 +143,7 @@ impl WhiteboxTool for LidarRemoveDuplicates {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/lidar_analysis/select_tiles_by_polygon.rs b/src/tools/lidar_analysis/select_tiles_by_polygon.rs
index f23dd7ee2..8a49d79ed 100644
--- a/src/tools/lidar_analysis/select_tiles_by_polygon.rs
+++ b/src/tools/lidar_analysis/select_tiles_by_polygon.rs
@@ -148,7 +148,7 @@ impl WhiteboxTool for SelectTilesByPolygon {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/math_stat_analysis/abs.rs b/src/tools/math_stat_analysis/abs.rs
index ffa0a6779..2624a72d8 100644
--- a/src/tools/math_stat_analysis/abs.rs
+++ b/src/tools/math_stat_analysis/abs.rs
@@ -131,7 +131,7 @@ impl WhiteboxTool for AbsoluteValue {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/math_stat_analysis/add.rs b/src/tools/math_stat_analysis/add.rs
index a4ebe749c..2aaee279d 100644
--- a/src/tools/math_stat_analysis/add.rs
+++ b/src/tools/math_stat_analysis/add.rs
@@ -141,7 +141,7 @@ impl WhiteboxTool for Add {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/math_stat_analysis/and.rs b/src/tools/math_stat_analysis/and.rs
index 20e5d493d..01625d04c 100644
--- a/src/tools/math_stat_analysis/and.rs
+++ b/src/tools/math_stat_analysis/and.rs
@@ -141,7 +141,7 @@ impl WhiteboxTool for And {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/math_stat_analysis/anova.rs b/src/tools/math_stat_analysis/anova.rs
index 6b88eaee5..17ca53eae 100644
--- a/src/tools/math_stat_analysis/anova.rs
+++ b/src/tools/math_stat_analysis/anova.rs
@@ -141,7 +141,7 @@ impl WhiteboxTool for Anova {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/math_stat_analysis/arccos.rs b/src/tools/math_stat_analysis/arccos.rs
index 5745c1c6b..3cd548a97 100644
--- a/src/tools/math_stat_analysis/arccos.rs
+++ b/src/tools/math_stat_analysis/arccos.rs
@@ -134,7 +134,7 @@ impl WhiteboxTool for ArcCos {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/math_stat_analysis/arcosh.rs b/src/tools/math_stat_analysis/arcosh.rs
index 2a324f7e8..37d00d56e 100644
--- a/src/tools/math_stat_analysis/arcosh.rs
+++ b/src/tools/math_stat_analysis/arcosh.rs
@@ -135,7 +135,7 @@ impl WhiteboxTool for Arcosh {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/math_stat_analysis/arcsin.rs b/src/tools/math_stat_analysis/arcsin.rs
index 4b999f176..eaa66624a 100644
--- a/src/tools/math_stat_analysis/arcsin.rs
+++ b/src/tools/math_stat_analysis/arcsin.rs
@@ -134,7 +134,7 @@ impl WhiteboxTool for ArcSin {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/math_stat_analysis/arctan.rs b/src/tools/math_stat_analysis/arctan.rs
index 2f68359f7..f66cbc0fa 100644
--- a/src/tools/math_stat_analysis/arctan.rs
+++ b/src/tools/math_stat_analysis/arctan.rs
@@ -134,7 +134,7 @@ impl WhiteboxTool for ArcTan {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/math_stat_analysis/arsinh.rs b/src/tools/math_stat_analysis/arsinh.rs
index 0199a5a89..b6568aa6d 100644
--- a/src/tools/math_stat_analysis/arsinh.rs
+++ b/src/tools/math_stat_analysis/arsinh.rs
@@ -135,7 +135,7 @@ impl WhiteboxTool for Arsinh {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/math_stat_analysis/artanh.rs b/src/tools/math_stat_analysis/artanh.rs
index 66bdafd8e..f99ff8c69 100644
--- a/src/tools/math_stat_analysis/artanh.rs
+++ b/src/tools/math_stat_analysis/artanh.rs
@@ -135,7 +135,7 @@ impl WhiteboxTool for Artanh {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/math_stat_analysis/atan2.rs b/src/tools/math_stat_analysis/atan2.rs
index d7c08d6ea..19dffe270 100644
--- a/src/tools/math_stat_analysis/atan2.rs
+++ b/src/tools/math_stat_analysis/atan2.rs
@@ -145,7 +145,7 @@ impl WhiteboxTool for Atan2 {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/math_stat_analysis/attribute_correlation.rs b/src/tools/math_stat_analysis/attribute_correlation.rs
index 97eb07bd2..0de61dc72 100644
--- a/src/tools/math_stat_analysis/attribute_correlation.rs
+++ b/src/tools/math_stat_analysis/attribute_correlation.rs
@@ -152,7 +152,7 @@ impl WhiteboxTool for AttributeCorrelation {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/math_stat_analysis/attribute_histogram.rs b/src/tools/math_stat_analysis/attribute_histogram.rs
index d33ee3410..b5949fc85 100644
--- a/src/tools/math_stat_analysis/attribute_histogram.rs
+++ b/src/tools/math_stat_analysis/attribute_histogram.rs
@@ -155,7 +155,7 @@ impl WhiteboxTool for AttributeHistogram {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/math_stat_analysis/attribute_scattergram.rs b/src/tools/math_stat_analysis/attribute_scattergram.rs
index d48bdc446..6048f2408 100644
--- a/src/tools/math_stat_analysis/attribute_scattergram.rs
+++ b/src/tools/math_stat_analysis/attribute_scattergram.rs
@@ -173,7 +173,7 @@ impl WhiteboxTool for AttributeScattergram {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/math_stat_analysis/ceil.rs b/src/tools/math_stat_analysis/ceil.rs
index 15d37132f..ab3e557e3 100644
--- a/src/tools/math_stat_analysis/ceil.rs
+++ b/src/tools/math_stat_analysis/ceil.rs
@@ -131,7 +131,7 @@ impl WhiteboxTool for Ceil {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/math_stat_analysis/cos.rs b/src/tools/math_stat_analysis/cos.rs
index 8000470be..3e70d8320 100644
--- a/src/tools/math_stat_analysis/cos.rs
+++ b/src/tools/math_stat_analysis/cos.rs
@@ -132,7 +132,7 @@ impl WhiteboxTool for Cos {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/math_stat_analysis/cosh.rs b/src/tools/math_stat_analysis/cosh.rs
index 0dc6e40cf..9f264172e 100644
--- a/src/tools/math_stat_analysis/cosh.rs
+++ b/src/tools/math_stat_analysis/cosh.rs
@@ -134,7 +134,7 @@ impl WhiteboxTool for Cosh {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/math_stat_analysis/crispness_index.rs b/src/tools/math_stat_analysis/crispness_index.rs
index 0c9ff2995..080648498 100644
--- a/src/tools/math_stat_analysis/crispness_index.rs
+++ b/src/tools/math_stat_analysis/crispness_index.rs
@@ -161,7 +161,7 @@ impl WhiteboxTool for CrispnessIndex {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/math_stat_analysis/cross_tabulation.rs b/src/tools/math_stat_analysis/cross_tabulation.rs
index b3b070d51..8d8ae79c5 100644
--- a/src/tools/math_stat_analysis/cross_tabulation.rs
+++ b/src/tools/math_stat_analysis/cross_tabulation.rs
@@ -147,7 +147,7 @@ impl WhiteboxTool for CrossTabulation {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/math_stat_analysis/cumulative_dist.rs b/src/tools/math_stat_analysis/cumulative_dist.rs
index fdc9dcefc..71dc90888 100644
--- a/src/tools/math_stat_analysis/cumulative_dist.rs
+++ b/src/tools/math_stat_analysis/cumulative_dist.rs
@@ -131,7 +131,7 @@ impl WhiteboxTool for CumulativeDistribution {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/math_stat_analysis/decrement.rs b/src/tools/math_stat_analysis/decrement.rs
index aa91de3c9..3dba08eb0 100644
--- a/src/tools/math_stat_analysis/decrement.rs
+++ b/src/tools/math_stat_analysis/decrement.rs
@@ -131,7 +131,7 @@ impl WhiteboxTool for Decrement {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/math_stat_analysis/divide.rs b/src/tools/math_stat_analysis/divide.rs
index adafb3c1e..6a00a8823 100644
--- a/src/tools/math_stat_analysis/divide.rs
+++ b/src/tools/math_stat_analysis/divide.rs
@@ -141,7 +141,7 @@ impl WhiteboxTool for Divide {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/math_stat_analysis/equal_to.rs b/src/tools/math_stat_analysis/equal_to.rs
index d56be3349..be4f5a2e6 100644
--- a/src/tools/math_stat_analysis/equal_to.rs
+++ b/src/tools/math_stat_analysis/equal_to.rs
@@ -138,7 +138,7 @@ impl WhiteboxTool for EqualTo {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/math_stat_analysis/exp.rs b/src/tools/math_stat_analysis/exp.rs
index 0c18cc633..6edd47b61 100644
--- a/src/tools/math_stat_analysis/exp.rs
+++ b/src/tools/math_stat_analysis/exp.rs
@@ -134,7 +134,7 @@ impl WhiteboxTool for Exp {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/math_stat_analysis/exp2.rs b/src/tools/math_stat_analysis/exp2.rs
index 35e3d9982..5c87d364a 100644
--- a/src/tools/math_stat_analysis/exp2.rs
+++ b/src/tools/math_stat_analysis/exp2.rs
@@ -133,7 +133,7 @@ impl WhiteboxTool for Exp2 {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/math_stat_analysis/floor.rs b/src/tools/math_stat_analysis/floor.rs
index 965b6cff5..9570c8826 100644
--- a/src/tools/math_stat_analysis/floor.rs
+++ b/src/tools/math_stat_analysis/floor.rs
@@ -131,7 +131,7 @@ impl WhiteboxTool for Floor {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/math_stat_analysis/greater_than.rs b/src/tools/math_stat_analysis/greater_than.rs
index 337cbd32a..d3f984641 100644
--- a/src/tools/math_stat_analysis/greater_than.rs
+++ b/src/tools/math_stat_analysis/greater_than.rs
@@ -151,7 +151,7 @@ impl WhiteboxTool for GreaterThan {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/math_stat_analysis/image_autocorrelation.rs b/src/tools/math_stat_analysis/image_autocorrelation.rs
index d6b758c5b..395e629b2 100644
--- a/src/tools/math_stat_analysis/image_autocorrelation.rs
+++ b/src/tools/math_stat_analysis/image_autocorrelation.rs
@@ -192,7 +192,7 @@ impl WhiteboxTool for ImageAutocorrelation {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/math_stat_analysis/image_correlation.rs b/src/tools/math_stat_analysis/image_correlation.rs
index 85724e04e..53396e53c 100644
--- a/src/tools/math_stat_analysis/image_correlation.rs
+++ b/src/tools/math_stat_analysis/image_correlation.rs
@@ -157,7 +157,7 @@ impl WhiteboxTool for ImageCorrelation {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/math_stat_analysis/image_correlation_neighbourhood_analysis.rs b/src/tools/math_stat_analysis/image_correlation_neighbourhood_analysis.rs
new file mode 100644
index 000000000..355192732
--- /dev/null
+++ b/src/tools/math_stat_analysis/image_correlation_neighbourhood_analysis.rs
@@ -0,0 +1,803 @@
+/*
+This tool is part of the WhiteboxTools geospatial analysis library.
+Authors: Simon Gudim and Dr. John Lindsay
+Created: 06/12/2019
+Last Modified: 06/12/2019
+License: MIT
+*/
+
+use std::cmp::Ordering::Equal;
+use std::env;
+use std::path;
+use std::f64;
+use std::sync::Arc;
+use std::sync::mpsc;
+use std::thread;
+use crate::raster::*;
+use std::io::{Error, ErrorKind};
+use crate::tools::*;
+use statrs::distribution::{StudentsT, Univariate};
+
+
+pub struct ImageCorrelationNeighbourhoodAnalysis {
+ name: String,
+ description: String,
+ toolbox: String,
+ parameters: Vec,
+ example_usage: String,
+}
+
+impl ImageCorrelationNeighbourhoodAnalysis {
+ pub fn new() -> ImageCorrelationNeighbourhoodAnalysis {
+ // public constructor
+ let name = "ImageCorrelationNeighbourhoodAnalysis".to_string();
+ let toolbox = "Math and Stats Tools".to_string();
+ let description = "Performs image correlation on two input images neighbourhood search windows.".to_string();
+
+ let mut parameters = vec![];
+ parameters.push(ToolParameter{
+ name: "Image 1".to_owned(),
+ flags: vec!["--i1".to_owned(), "--input1".to_owned()],
+ description: "Input raster file.".to_owned(),
+ parameter_type: ParameterType::ExistingFile(ParameterFileType::Raster),
+ default_value: None,
+ optional: false
+ });
+
+ parameters.push(ToolParameter{
+ name: "Image 2".to_owned(),
+ flags: vec!["--i2".to_owned(), "--input2".to_owned()],
+ description: "Input raster file.".to_owned(),
+ parameter_type: ParameterType::ExistingFile(ParameterFileType::Raster),
+ default_value: None,
+ optional: false
+ });
+
+ parameters.push(ToolParameter{
+ name: "Output Correlation File".to_owned(),
+ flags: vec!["--o1".to_owned(), "--output1".to_owned()],
+ description: "Output correlation (r-value or rho) raster file.".to_owned(),
+ parameter_type: ParameterType::NewFile(ParameterFileType::Raster),
+ default_value: None,
+ optional: false
+ });
+
+ parameters.push(ToolParameter{
+ name: "Output Significance File".to_owned(),
+ flags: vec!["--o2".to_owned(), "--output2".to_owned()],
+ description: "Output significance (p-value) raster file.".to_owned(),
+ parameter_type: ParameterType::NewFile(ParameterFileType::Raster),
+ default_value: None,
+ optional: false
+ });
+
+ parameters.push(ToolParameter{
+ name: "Filter Size".to_owned(),
+ flags: vec!["--filter".to_owned()],
+ description: "Size of the filter kernel.".to_owned(),
+ parameter_type: ParameterType::Integer,
+ default_value: Some("11".to_owned()),
+ optional: true
+ });
+
+ parameters.push(ToolParameter {
+ name: "Correlation Statistic Type".to_owned(),
+ flags: vec!["--stat".to_owned()],
+ description: "Correlation type; one of 'pearson' (default) and 'spearman'.".to_owned(),
+ parameter_type: ParameterType::OptionList(vec!["pearson".to_owned(), "kendall".to_owned(), "spearman".to_owned()]),
+ default_value: Some("pearson".to_owned()),
+ optional: true,
+ });
+
+ let sep: String = path::MAIN_SEPARATOR.to_string();
+ let p = format!("{}", env::current_dir().unwrap().display());
+ let e = format!("{}", env::current_exe().unwrap().display());
+ let mut short_exe = e.replace(&p, "")
+ .replace(".exe", "")
+ .replace(".", "")
+ .replace(&sep, "");
+ if e.contains(".exe") {
+ short_exe += ".exe";
+ }
+ let usage = format!(">>.*{0} -r={1} -v --wd=\"*path*to*data*\" --i1=file1.tif --i2=file2.tif --o1=corr.tif --o2=sig.tif --stat=\"spearman\"",
+ short_exe,
+ name)
+ .replace("*", &sep);
+
+ ImageCorrelationNeighbourhoodAnalysis {
+ name: name,
+ description: description,
+ toolbox: toolbox,
+ parameters: parameters,
+ example_usage: usage,
+ }
+ }
+}
+
+impl WhiteboxTool for ImageCorrelationNeighbourhoodAnalysis {
+ fn get_source_file(&self) -> String {
+ String::from(file!())
+ }
+
+ fn get_tool_name(&self) -> String {
+ self.name.clone()
+ }
+
+ fn get_tool_description(&self) -> String {
+ self.description.clone()
+ }
+
+ fn get_tool_parameters(&self) -> String {
+ let mut s = String::from("{\"parameters\": [");
+ for i in 0..self.parameters.len() {
+ if i < self.parameters.len() - 1 {
+ s.push_str(&(self.parameters[i].to_string()));
+ s.push_str(",");
+ } else {
+ s.push_str(&(self.parameters[i].to_string()));
+ }
+ }
+ s.push_str("]}");
+ s
+ }
+
+ fn get_example_usage(&self) -> String {
+ self.example_usage.clone()
+ }
+
+ fn get_toolbox(&self) -> String {
+ self.toolbox.clone()
+ }
+
+ fn run<'a>(&self,
+ args: Vec,
+ working_directory: &'a str,
+ verbose: bool)
+ -> Result<(), Error> {
+ let mut input_file1 = String::new();
+ let mut input_file2 = String::new();
+ let mut output_file1 = String::new();
+ let mut output_file2 = String::new();
+ let mut filter_size = 11usize;
+ let mut stat_type = String::from("pearson");
+
+ if args.len() == 0 {
+ return Err(Error::new(ErrorKind::InvalidInput, "Tool run with no parameters."));
+ }
+
+ for i in 0..args.len() {
+ let mut arg = args[i].replace("\"", "");
+ arg = arg.replace("\'", "");
+ let cmd = arg.split("="); // in case an equals sign was used
+ let vec = cmd.collect::>();
+ let mut keyval = false;
+ if vec.len() > 1 {
+ keyval = true;
+ }
+ let flag_val = vec[0].to_lowercase().replace("--", "-");
+ if flag_val == "-i1" || flag_val == "-input1" {
+ input_file1 = if keyval {
+ vec[1].to_string()
+ } else {
+ args[i+1].to_string()
+ };
+ } else if flag_val == "-i2" || flag_val == "-input2" {
+ input_file2 = if keyval {
+ vec[1].to_string()
+ } else {
+ args[i+1].to_string()
+ };
+ } else if flag_val == "-o1" || flag_val == "-output1" {
+ output_file1 = if keyval {
+ vec[1].to_string()
+ } else {
+ args[i + 1].to_string()
+ };
+ } else if flag_val == "-o2" || flag_val == "-output2" {
+ output_file2 = if keyval {
+ vec[1].to_string()
+ } else {
+ args[i + 1].to_string()
+ };
+ } else if flag_val == "-stat" {
+ let val = if keyval {
+ vec[1].to_lowercase()
+ } else {
+ args[i + 1].to_lowercase()
+ };
+ stat_type = if val.contains("son") {
+ "pearson".to_string()
+ } else if val.contains("kendall") {
+ "kendall".to_string()
+ } else {
+ "spearman".to_string()
+ };
+ } else if vec[0].to_lowercase() == "-filter" || vec[0].to_lowercase() == "--filter" {
+ if keyval {
+ filter_size = vec[1].to_string().parse::().unwrap() as usize;
+ } else {
+ filter_size = args[i+1].to_string().parse::().unwrap() as usize;
+ }
+ }
+ }
+
+
+ if verbose {
+ println!("***************{}", "*".repeat(self.get_tool_name().len()));
+ println!("* Welcome to {} *", self.get_tool_name());
+ println!("***************{}", "*".repeat(self.get_tool_name().len()));
+ }
+
+ if filter_size < 3 { filter_size = 3; }
+ let sep: String = path::MAIN_SEPARATOR.to_string();
+
+ let mut progress: usize;
+ let mut old_progress: usize = 1;
+
+ //let start = time::now();
+ let start = Instant::now(); // had to change to this from old time::now
+
+ if !input_file1.contains(&sep) && !input_file1.contains("/") {
+ input_file1 = format!("{}{}", working_directory, input_file1);
+ }
+ if !input_file2.contains(&sep) && !input_file2.contains("/") {
+ input_file2 = format!("{}{}", working_directory, input_file2);
+ }
+
+
+ if !output_file1.contains(&sep) && !output_file1.contains("/") {
+ output_file1 = format!("{}{}", working_directory, output_file1);
+ }
+
+ if !output_file2.contains(&sep) && !output_file2.contains("/") {
+ output_file2 = format!("{}{}", working_directory, output_file2);
+ }
+
+ let num_procs = num_cpus::get() as isize;
+
+ if verbose { println!("Reading data...") };
+
+ let image1 = Arc::new(Raster::new(&input_file1, "r")?);
+ let rows = image1.configs.rows as isize;
+ let columns = image1.configs.columns as isize;
+ let nodata1 = image1.configs.nodata;
+
+ let image2 = Arc::new(Raster::new(&input_file2, "r")?);
+ if image2.configs.rows as isize != rows || image2.configs.columns as isize != columns {
+ panic!("Error: The input files do not contain the same raster extent.");
+ }
+ let nodata2 = image2.configs.nodata;
+
+ // The r-value output
+ let mut output_val = Raster::initialize_using_file(&output_file1, &image1);
+ // The significance (p-value) output
+ let mut output_sig = Raster::initialize_using_file(&output_file2, &image1);
+
+ // Pearson's Correlation //
+ if stat_type == "pearson" {
+ let (tx, rx) = mpsc::channel();
+ for tid in 0..num_procs {
+ let image1 = image1.clone();
+ let image2 = image2.clone();
+ let tx = tx.clone();
+ thread::spawn(move || {
+ let mut num_cells: usize;
+ let mut sum1: f64;
+ let mut sum2: f64;
+ let mut total_deviation1: f64;
+ let mut total_deviation2: f64;
+ let mut product_deviations: f64;
+ let (mut mean1, mut mean2): (f64, f64);
+ let mut r: f64;
+ let mut df: usize;
+ let mut tvalue: f64;
+ let mut pvalue: f64;
+ let (mut z1, mut z2): (f64, f64);
+ let (mut z_n1, mut z_n2): (f64, f64);
+ let num_pixels_in_filter = filter_size * filter_size;
+ let mut dx = vec![0isize; num_pixels_in_filter];
+ let mut dy = vec![0isize; num_pixels_in_filter];
+
+ // fill the filter d_x and d_y values
+ let midpoint: isize = (filter_size as f64 / 2f64).floor() as isize; // + 1;
+ let mut a = 0;
+ for row in 0..filter_size {
+ for col in 0..filter_size {
+ dx[a] = col as isize - midpoint;
+ dy[a] = row as isize - midpoint;
+ a += 1;
+ }
+ }
+
+ for row in (0..rows).filter(|r| r % num_procs == tid) {
+ let mut data1 = vec![nodata1; columns as usize];
+ let mut data2 = vec![nodata1; columns as usize];
+ for col in 0..columns {
+ z1 = image1.get_value(row, col);
+ z2 = image2.get_value(row, col);
+ if z1 != nodata1 && z2 != nodata2 {
+ // First, calculate the mean
+ num_cells = 0;
+ sum1 = 0f64;
+ sum2 = 0f64;
+ for i in 0..num_pixels_in_filter {
+ z_n1 = image1.get_value(row + dy[i], col + dx[i]);
+ z_n2 = image2.get_value(row + dy[i], col + dx[i]);
+ if z_n1 != nodata1 && z_n2 != nodata2 {
+ sum1 += z_n1;
+ sum2 += z_n2;
+ num_cells += 1;
+ }
+ }
+ mean1 = sum1 / num_cells as f64;
+ mean2 = sum2 / num_cells as f64;
+
+ // Now calculate the total deviations and total cross-deviation.
+ total_deviation1 = 0f64;
+ total_deviation2 = 0f64;
+ product_deviations = 0f64;
+ if num_cells > 2 {
+ for i in 0..num_pixels_in_filter {
+ z_n1 = image1.get_value(row + dy[i], col + dx[i]);
+ z_n2 = image2.get_value(row + dy[i], col + dx[i]);
+ if z_n1 != nodata1 && z_n2 != nodata2 {
+ total_deviation1 += (z_n1 - mean1) * (z_n1 - mean1);
+ total_deviation2 += (z_n2 - mean2) * (z_n2 - mean2);
+ product_deviations += (z_n1 - mean1) * (z_n2 - mean2);
+ }
+ }
+ }
+
+ // Finally, calculate r for the neighbourhood.
+ r = if total_deviation1 != 0f64 && total_deviation2 != 0f64 && num_cells > 2 {
+ product_deviations / (total_deviation1 * total_deviation2).sqrt()
+ } else {
+ // You can't divide by zero
+ 0f64
+ };
+
+ data1[col as usize] = r;
+
+ df = num_cells - 2;
+ if df > 2 {
+ tvalue = r * (df as f64 / (1f64 - r * r)).sqrt();
+ let t = StudentsT::new(0.0, 1.0, df as f64).unwrap();
+ pvalue = 2f64 * (1f64 - t.cdf(tvalue.abs()));
+ data2[col as usize] = pvalue;
+ } else {
+ data2[col as usize] = 0f64;
+ }
+ }
+ }
+ tx.send((row, data1, data2)).unwrap();
+ }
+ });
+ }
+
+ for r in 0..rows {
+ let (row, data1, data2) = rx.recv().unwrap();
+ output_val.set_row_data(row, data1);
+ output_sig.set_row_data(row, data2);
+
+ if verbose {
+ progress = (100.0_f64 * r as f64 / (rows - 1) as f64) as usize;
+ if progress != old_progress {
+ println!("Performing Correlation: {}%", progress);
+ old_progress = progress;
+ }
+ }
+ }
+ } else if stat_type == "kendall" { // Perform Kendall's Tau-b correlation
+ let (tx, rx) = mpsc::channel();
+ for tid in 0..num_procs {
+ let image1 = image1.clone();
+ let image2 = image2.clone();
+ let tx = tx.clone();
+ thread::spawn(move || {
+ let mut num_cells: usize;
+ let mut num_cells_f64: f64;
+ let mut tau: f64;
+ let mut df: f64;
+ let mut zvalue: f64;
+ let mut pvalue: f64;
+ let (mut z1, mut z2): (f64, f64);
+ let (mut z_n1, mut z_n2): (f64, f64);
+ let num_pixels_in_filter = filter_size * filter_size;
+ let mut dx = vec![0isize; num_pixels_in_filter];
+ let mut dy = vec![0isize; num_pixels_in_filter];
+ let (mut rank, mut rank2): (f64, f64);
+ let mut upper_range: usize;
+
+ let mut num_tied_vals: f64;
+ let mut nt1: f64;
+ let mut nt2: f64;
+ let mut n0: f64;
+ let mut numer: f64;
+
+ let midpoint: isize = (filter_size as f64 / 2f64).floor() as isize; // + 1;
+ let mut a = 0;
+ for row in 0..filter_size {
+ for col in 0..filter_size {
+ dx[a] = col as isize - midpoint;
+ dy[a] = row as isize - midpoint;
+ a += 1;
+ }
+ }
+
+ for row in (0..rows).filter(|r| r % num_procs == tid) {
+ let mut data1 = vec![nodata1; columns as usize];
+ let mut data2 = vec![nodata1; columns as usize];
+ for col in 0..columns {
+ z1 = image1.get_value(row, col);
+ z2 = image2.get_value(row, col);
+ if z1 != nodata1 && z2 != nodata2 {
+ let mut v1 = Vec::with_capacity(num_pixels_in_filter);
+ let mut v2 = Vec::with_capacity(num_pixels_in_filter);
+ num_cells = 0;
+ for i in 0..num_pixels_in_filter {
+ z_n1 = image1.get_value(row + dy[i], col + dx[i]);
+ z_n2 = image2.get_value(row + dy[i], col + dx[i]);
+ if z_n1 != nodata1 && z_n2 != nodata2 {
+ num_cells += 1;
+ // tuple = (value, index, rank)
+ v1.push((z_n1, num_cells, 0f64));
+ v2.push((z_n2, num_cells, 0f64));
+ }
+ }
+ num_cells_f64 = num_cells as f64;
+
+ // Sort both lists based on value
+ v1.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap_or(Equal));
+ v2.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap_or(Equal));
+
+ // Now provide the rank data
+ rank = 0f64;
+ nt1 = 0f64;
+ for i in 0..num_cells {
+ if v1[i].2 == 0f64 {
+ rank += 1f64;
+ if i < num_cells - 1 {
+ // are there any ties above this one?
+ upper_range = i;
+ for j in i+1..num_cells {
+ if v1[i].0 == v1[j].0 {
+ upper_range = j;
+ } else {
+ break;
+ }
+ }
+ if upper_range != i {
+ num_tied_vals = (upper_range - i + 1) as f64;
+ nt1 += num_tied_vals * (num_tied_vals - 1f64) / 2f64;
+ rank2 = rank + (upper_range - i) as f64;
+ rank = (rank + rank2) / 2f64; // average rank
+ for k in i..=upper_range {
+ v1[k].2 = rank;
+ }
+ rank = rank2;
+ } else {
+ v1[i].2 = rank;
+ }
+ } else {
+ v1[i].2 = rank;
+ }
+ }
+ }
+
+ nt2 = 0f64;
+ rank = 0f64;
+ for i in 0..num_cells {
+ if v2[i].2 == 0f64 {
+ rank += 1f64;
+ if i < num_cells - 1 {
+ // are there any ties above this one?
+ upper_range = i;
+ for j in i+1..num_cells {
+ if v2[i].0 == v2[j].0 {
+ upper_range = j;
+ } else {
+ break;
+ }
+ }
+ if upper_range != i {
+ num_tied_vals = (upper_range - i + 1) as f64;
+ nt2 += num_tied_vals * (num_tied_vals - 1f64) / 2f64;
+ rank2 = rank + (upper_range - i) as f64;
+ rank = (rank + rank2) / 2f64; // average rank
+ for k in i..=upper_range {
+ v2[k].2 = rank;
+ }
+ rank = rank2;
+ } else {
+ v2[i].2 = rank;
+ }
+ } else {
+ v2[i].2 = rank;
+ }
+ }
+ }
+
+ // Sort both lists based on index
+ v1.sort_by(|a, b| a.1.cmp(&b.1));
+ v2.sort_by(|a, b| a.1.cmp(&b.1));
+
+ ////////////////////////////////////////////////////////////////////////////
+ // This block of code is O(n^2) and is a serious performance killer. There
+ // is a O(nlogn) solution based on swaps in a merge-sort but I have yet to
+ // figure it out. As it stands, this solution is unacceptable for search
+ // windows larger than about 25, depending the number of cores in the
+ // system processor.
+ ////////////////////////////////////////////////////////////////////////////
+ numer = 0f64;
+ for i in 0..num_cells {
+ for j in i+1..num_cells {
+ if v1[i].2 != v1[j].2 && v2[i].2 != v2[j].2 {
+ numer += (v1[i].2 - v1[j].2).signum() * (v2[i].2 - v2[j].2).signum();
+ }
+ }
+ }
+
+ n0 = num_cells as f64 * (num_cells as f64 - 1f64) / 2f64;
+ tau = numer / ((n0 - nt1)*(n0 - nt2)).sqrt();
+ data1[col as usize] = tau;
+ df = num_cells_f64 - 2f64;
+
+ if df > 2f64 {
+ zvalue = 3f64 * numer / (num_cells_f64*(num_cells_f64-1f64)*(2f64*num_cells_f64+5f64) / 2f64).sqrt();
+ let t = StudentsT::new(0.0, 1.0, df as f64).unwrap(); // create a student's t distribution
+ pvalue = 2f64 * (1f64 - t.cdf(zvalue.abs())); // calculate the p-value (significance)
+ data2[col as usize] = pvalue;
+ } else {
+ data2[col as usize] = 0f64;
+ }
+ }
+ }
+ tx.send((row, data1, data2)).unwrap();
+ }
+ });
+ }
+
+ for r in 0..rows {
+ let (row, data1, data2) = rx.recv().unwrap();
+ output_val.set_row_data(row, data1);
+ output_sig.set_row_data(row, data2);
+
+ if verbose {
+ progress = (100.0_f64 * r as f64 / (rows - 1) as f64) as usize;
+ if progress != old_progress {
+ println!("Performing Correlation: {}%", progress);
+ old_progress = progress;
+ }
+ }
+ }
+ } else { // Calculate Spearman's Rho correlation
+ let (tx, rx) = mpsc::channel();
+ for tid in 0..num_procs {
+ let image1 = image1.clone();
+ let image2 = image2.clone();
+ let tx = tx.clone();
+ thread::spawn(move || {
+ let mut num_cells: usize;
+ let mut num_cells_f64: f64;
+ let mut rho: f64;
+ let mut df: f64;
+ let mut tvalue: f64;
+ let mut pvalue: f64;
+ let (mut z1, mut z2): (f64, f64);
+ let (mut z_n1, mut z_n2): (f64, f64);
+ let num_pixels_in_filter = filter_size * filter_size;
+ let mut dx = vec![0isize; num_pixels_in_filter];
+ let mut dy = vec![0isize; num_pixels_in_filter];
+ let (mut rank, mut rank2): (f64, f64);
+ let mut upper_range: usize;
+ let mut num_ties = 0;
+ let midpoint: isize = (filter_size as f64 / 2f64).floor() as isize; // + 1;
+ let mut a = 0;
+ for row in 0..filter_size {
+ for col in 0..filter_size {
+ dx[a] = col as isize - midpoint;
+ dy[a] = row as isize - midpoint;
+ a += 1;
+ }
+ }
+
+ for row in (0..rows).filter(|r| r % num_procs == tid) {
+ let mut data1 = vec![nodata1; columns as usize];
+ let mut data2 = vec![nodata1; columns as usize];
+ for col in 0..columns {
+ z1 = image1.get_value(row, col);
+ z2 = image2.get_value(row, col);
+ if z1 != nodata1 && z2 != nodata2 {
+ let mut v1 = Vec::with_capacity(num_pixels_in_filter);
+ let mut v2 = Vec::with_capacity(num_pixels_in_filter);
+ num_cells = 0;
+ for i in 0..num_pixels_in_filter {
+ z_n1 = image1.get_value(row + dy[i], col + dx[i]);
+ z_n2 = image2.get_value(row + dy[i], col + dx[i]);
+ if z_n1 != nodata1 && z_n2 != nodata2 {
+ num_cells += 1;
+ // tuple = (value, index, rank)
+ v1.push((z_n1, num_cells, 0f64));
+ v2.push((z_n2, num_cells, 0f64));
+ }
+ }
+ num_cells_f64 = num_cells as f64;
+
+ // Sort both lists based on value
+ v1.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap_or(Equal));
+ v2.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap_or(Equal));
+
+ rank = 0f64;
+ for i in 0..num_cells {
+ if v1[i].2 == 0f64 {
+ rank += 1f64;
+ if i < num_cells - 1 {
+ // are there any ties above this one?
+ upper_range = i;
+ for j in i+1..num_cells {
+ if v1[i].0 == v1[j].0 {
+ upper_range = j;
+ num_ties += 1;
+ } else {
+ break;
+ }
+ }
+ if upper_range != i {
+ rank2 = rank + (upper_range - i) as f64;
+ rank = (rank + rank2) / 2f64; // average rank
+ for k in i..=upper_range {
+ v1[k].2 = rank;
+ }
+ rank = rank2;
+ } else {
+ v1[i].2 = rank;
+ }
+ } else {
+ v1[i].2 = rank;
+ }
+ }
+ }
+
+ rank = 0f64;
+ for i in 0..num_cells {
+ if v2[i].2 == 0f64 {
+ rank += 1f64;
+ if i < num_cells - 1 {
+ // are there any ties above this one?
+ upper_range = i;
+ for j in i+1..num_cells {
+ if v2[i].0 == v2[j].0 {
+ upper_range = j;
+ num_ties += 1;
+ } else {
+ break;
+ }
+ }
+ if upper_range != i {
+ rank2 = rank + (upper_range - i) as f64;
+ rank = (rank + rank2) / 2f64; // average rank
+ for k in i..=upper_range {
+ v2[k].2 = rank;
+ }
+ rank = rank2;
+ } else {
+ v2[i].2 = rank;
+ }
+ } else {
+ v2[i].2 = rank;
+ }
+ }
+ }
+
+ // Sort both lists based on index
+ v1.sort_by(|a, b| a.1.cmp(&b.1));
+ v2.sort_by(|a, b| a.1.cmp(&b.1));
+
+ let mut rank_diff_sqrd = 0f64;
+ for i in 0..num_cells {
+ rank_diff_sqrd += (v1[i].2 - v2[i].2) * (v1[i].2 - v2[i].2);
+ }
+
+ rho = 1f64 - (6f64 * rank_diff_sqrd / (num_cells_f64 * num_cells_f64 * num_cells_f64 - num_cells_f64));
+ data1[col as usize] = rho;
+ df = num_cells_f64 - 2f64; // calculate degrees of freedom (Anthony Comment)
+
+ if df > 2f64 {
+ tvalue = rho * (df / (1f64 - rho * rho)).sqrt();
+ let t = StudentsT::new(0.0, 1.0, df as f64).unwrap(); // create a student's t distribution
+ pvalue = 2f64 * (1f64 - t.cdf(tvalue.abs())); // calculate the p-value (significance)
+ data2[col as usize] = pvalue;
+ } else {
+ data2[col as usize] = 0f64;
+ }
+ }
+ }
+ tx.send((row, data1, data2, num_ties)).unwrap();
+ }
+ });
+ }
+
+ let mut num_ties = 0;
+ for r in 0..rows {
+ let (row, data1, data2, ties) = rx.recv().unwrap();
+ output_val.set_row_data(row, data1);
+ output_sig.set_row_data(row, data2);
+ num_ties += ties;
+
+ if verbose {
+ progress = (100.0_f64 * r as f64 / (rows - 1) as f64) as usize;
+ if progress != old_progress {
+ println!("Performing Correlation: {}%", progress);
+ old_progress = progress;
+ }
+ }
+ }
+
+ if num_ties > 0 {
+ println!("Warning: There were ties in the tests and as a result p-values \nmay be misleading. Use Kendall's Tau instead.");
+ }
+ }
+
+ let elapsed_time = get_formatted_elapsed_time(start);
+
+ output_val.add_metadata_entry(format!("Created by whitebox_tools\' {} tool", self.get_tool_name()));
+ output_val.add_metadata_entry(format!("Filter size: {}", filter_size));
+ output_val.add_metadata_entry(format!("Input file 1: {}", input_file1));
+ output_val.add_metadata_entry(format!("Input file 2: {}", input_file2));
+ output_val.add_metadata_entry(format!("Statistic: {}", stat_type));
+ output_val.add_metadata_entry(format!("Elapsed Time (excluding I/O): {}", elapsed_time).replace("PT", ""));
+
+ if verbose { println!("Saving data...") };
+ let _ = match output_val.write() {
+ Ok(_) => if verbose { println!("Output file written") },
+ Err(e) => return Err(e),
+ };
+
+ output_sig.add_metadata_entry(format!("Created by whitebox_tools\' {} tool", self.get_tool_name()));
+ output_sig.add_metadata_entry(format!("Filter size: {}", filter_size));
+ output_sig.add_metadata_entry(format!("Input file 1: {}", input_file1));
+ output_sig.add_metadata_entry(format!("Input file 2: {}", input_file2));
+ output_val.add_metadata_entry(format!("Statistic: {}", stat_type));
+ output_sig.add_metadata_entry(format!("Elapsed Time (excluding I/O): {}", elapsed_time).replace("PT", ""));
+
+ let _ = match output_sig.write() {
+ Ok(_) => if verbose { println!(" ") },
+ Err(e) => return Err(e),
+ };
+
+ if verbose {
+ println!(
+ "{}",
+ &format!("Elapsed Time (excluding I/O): {}", elapsed_time)
+ );
+ }
+
+ // println!("Number of total cells: {}", valid);
+ // println!("Numbed of significant cells: {}", sig);
+ // println!("Numbed of significant negative cells: {}", sig_neg);
+ // println!("Numbed of significant postive cells: {}", sig_pos);
+
+ Ok(())
+ }
+
+}
+// #[derive(PartialEq, Debug)]
+// struct GridCell {
+// pub z: f64,
+// pub index: usize,
+// pub rank: f64,
+// }
+
+// impl Eq for GridCell {}
+
+// impl PartialOrd for GridCell {
+// fn partial_cmp(&self, other: &Self) -> Option {
+// self.z.partial_cmp(&other.z)
+// }
+// }
+
+// impl Ord for GridCell {
+// fn cmp(&self, other: &GridCell) -> Ordering {
+// self.partial_cmp(other).unwrap()
+// }
+// }
diff --git a/src/tools/math_stat_analysis/image_regression.rs b/src/tools/math_stat_analysis/image_regression.rs
index 5d0cbff1c..959a58e34 100644
--- a/src/tools/math_stat_analysis/image_regression.rs
+++ b/src/tools/math_stat_analysis/image_regression.rs
@@ -203,7 +203,7 @@ impl WhiteboxTool for ImageRegression {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/math_stat_analysis/increment.rs b/src/tools/math_stat_analysis/increment.rs
index 28cd42cd0..62eae7be5 100644
--- a/src/tools/math_stat_analysis/increment.rs
+++ b/src/tools/math_stat_analysis/increment.rs
@@ -131,7 +131,7 @@ impl WhiteboxTool for Increment {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/math_stat_analysis/inplace_add.rs b/src/tools/math_stat_analysis/inplace_add.rs
index 451ea90f0..6b027b062 100644
--- a/src/tools/math_stat_analysis/inplace_add.rs
+++ b/src/tools/math_stat_analysis/inplace_add.rs
@@ -136,7 +136,7 @@ impl WhiteboxTool for InPlaceAdd {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/math_stat_analysis/inplace_divide.rs b/src/tools/math_stat_analysis/inplace_divide.rs
index b0d5616e5..e5ef371b8 100644
--- a/src/tools/math_stat_analysis/inplace_divide.rs
+++ b/src/tools/math_stat_analysis/inplace_divide.rs
@@ -135,7 +135,7 @@ impl WhiteboxTool for InPlaceDivide {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/math_stat_analysis/inplace_multiply.rs b/src/tools/math_stat_analysis/inplace_multiply.rs
index 77bfcb029..bcf6c3253 100644
--- a/src/tools/math_stat_analysis/inplace_multiply.rs
+++ b/src/tools/math_stat_analysis/inplace_multiply.rs
@@ -135,7 +135,7 @@ impl WhiteboxTool for InPlaceMultiply {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/math_stat_analysis/inplace_subtract.rs b/src/tools/math_stat_analysis/inplace_subtract.rs
index fcfd13ca2..e582ad123 100644
--- a/src/tools/math_stat_analysis/inplace_subtract.rs
+++ b/src/tools/math_stat_analysis/inplace_subtract.rs
@@ -137,7 +137,7 @@ impl WhiteboxTool for InPlaceSubtract {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/math_stat_analysis/integer_division.rs b/src/tools/math_stat_analysis/integer_division.rs
index 717b2155a..aea97fb95 100644
--- a/src/tools/math_stat_analysis/integer_division.rs
+++ b/src/tools/math_stat_analysis/integer_division.rs
@@ -141,7 +141,7 @@ impl WhiteboxTool for IntegerDivision {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/math_stat_analysis/isnodata.rs b/src/tools/math_stat_analysis/isnodata.rs
index 07f91110d..107586854 100644
--- a/src/tools/math_stat_analysis/isnodata.rs
+++ b/src/tools/math_stat_analysis/isnodata.rs
@@ -132,7 +132,7 @@ impl WhiteboxTool for IsNoData {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/math_stat_analysis/kappa_index.rs b/src/tools/math_stat_analysis/kappa_index.rs
index 26454ef17..11a03529f 100644
--- a/src/tools/math_stat_analysis/kappa_index.rs
+++ b/src/tools/math_stat_analysis/kappa_index.rs
@@ -149,7 +149,7 @@ impl WhiteboxTool for KappaIndex {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/math_stat_analysis/ks_normality_test.rs b/src/tools/math_stat_analysis/ks_normality_test.rs
index 0135b2cca..784788d12 100644
--- a/src/tools/math_stat_analysis/ks_normality_test.rs
+++ b/src/tools/math_stat_analysis/ks_normality_test.rs
@@ -147,7 +147,7 @@ impl WhiteboxTool for KsTestForNormality {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/math_stat_analysis/less_than.rs b/src/tools/math_stat_analysis/less_than.rs
index 017f776ad..2f3a6f11f 100644
--- a/src/tools/math_stat_analysis/less_than.rs
+++ b/src/tools/math_stat_analysis/less_than.rs
@@ -151,7 +151,7 @@ impl WhiteboxTool for LessThan {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/math_stat_analysis/list_unique_values.rs b/src/tools/math_stat_analysis/list_unique_values.rs
index 91a268a19..a0e4e43e6 100644
--- a/src/tools/math_stat_analysis/list_unique_values.rs
+++ b/src/tools/math_stat_analysis/list_unique_values.rs
@@ -157,7 +157,7 @@ impl WhiteboxTool for ListUniqueValues {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/math_stat_analysis/ln.rs b/src/tools/math_stat_analysis/ln.rs
index 62def02f1..dcc112d9f 100644
--- a/src/tools/math_stat_analysis/ln.rs
+++ b/src/tools/math_stat_analysis/ln.rs
@@ -134,7 +134,7 @@ impl WhiteboxTool for Ln {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/math_stat_analysis/log10.rs b/src/tools/math_stat_analysis/log10.rs
index 92d242c9c..45981e6d3 100644
--- a/src/tools/math_stat_analysis/log10.rs
+++ b/src/tools/math_stat_analysis/log10.rs
@@ -132,7 +132,7 @@ impl WhiteboxTool for Log10 {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/math_stat_analysis/log2.rs b/src/tools/math_stat_analysis/log2.rs
index e012c8bea..2f938ddca 100644
--- a/src/tools/math_stat_analysis/log2.rs
+++ b/src/tools/math_stat_analysis/log2.rs
@@ -132,7 +132,7 @@ impl WhiteboxTool for Log2 {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/math_stat_analysis/max.rs b/src/tools/math_stat_analysis/max.rs
index 99df42e1e..bf3006cdb 100644
--- a/src/tools/math_stat_analysis/max.rs
+++ b/src/tools/math_stat_analysis/max.rs
@@ -139,7 +139,7 @@ impl WhiteboxTool for Max {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/math_stat_analysis/min.rs b/src/tools/math_stat_analysis/min.rs
index cd215167d..539e5e5c6 100644
--- a/src/tools/math_stat_analysis/min.rs
+++ b/src/tools/math_stat_analysis/min.rs
@@ -139,7 +139,7 @@ impl WhiteboxTool for Min {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/math_stat_analysis/mod.rs b/src/tools/math_stat_analysis/mod.rs
index e9b65ef98..9c6333c58 100644
--- a/src/tools/math_stat_analysis/mod.rs
+++ b/src/tools/math_stat_analysis/mod.rs
@@ -29,6 +29,7 @@ mod floor;
mod greater_than;
mod image_autocorrelation;
mod image_correlation;
+mod image_correlation_neighbourhood_analysis;
mod image_regression;
mod increment;
mod inplace_add;
@@ -113,6 +114,7 @@ pub use self::floor::Floor;
pub use self::greater_than::GreaterThan;
pub use self::image_autocorrelation::ImageAutocorrelation;
pub use self::image_correlation::ImageCorrelation;
+pub use self::image_correlation_neighbourhood_analysis::ImageCorrelationNeighbourhoodAnalysis;
pub use self::image_regression::ImageRegression;
pub use self::increment::Increment;
pub use self::inplace_add::InPlaceAdd;
diff --git a/src/tools/math_stat_analysis/modulo.rs b/src/tools/math_stat_analysis/modulo.rs
index b778149ee..65d5196cd 100644
--- a/src/tools/math_stat_analysis/modulo.rs
+++ b/src/tools/math_stat_analysis/modulo.rs
@@ -146,7 +146,7 @@ impl WhiteboxTool for Modulo {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/math_stat_analysis/multiply.rs b/src/tools/math_stat_analysis/multiply.rs
index 21f2e955e..f07b2fb22 100644
--- a/src/tools/math_stat_analysis/multiply.rs
+++ b/src/tools/math_stat_analysis/multiply.rs
@@ -141,7 +141,7 @@ impl WhiteboxTool for Multiply {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/math_stat_analysis/negate.rs b/src/tools/math_stat_analysis/negate.rs
index dd7a843a2..54d2ea816 100644
--- a/src/tools/math_stat_analysis/negate.rs
+++ b/src/tools/math_stat_analysis/negate.rs
@@ -136,7 +136,7 @@ impl WhiteboxTool for Negate {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/math_stat_analysis/not.rs b/src/tools/math_stat_analysis/not.rs
index e84cdcc33..7f529b02d 100644
--- a/src/tools/math_stat_analysis/not.rs
+++ b/src/tools/math_stat_analysis/not.rs
@@ -141,7 +141,7 @@ impl WhiteboxTool for Not {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/math_stat_analysis/not_equal_to.rs b/src/tools/math_stat_analysis/not_equal_to.rs
index d5ec335c8..6ba53ca0b 100644
--- a/src/tools/math_stat_analysis/not_equal_to.rs
+++ b/src/tools/math_stat_analysis/not_equal_to.rs
@@ -138,7 +138,7 @@ impl WhiteboxTool for NotEqualTo {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/math_stat_analysis/or.rs b/src/tools/math_stat_analysis/or.rs
index a42ea61d7..54e97cfeb 100644
--- a/src/tools/math_stat_analysis/or.rs
+++ b/src/tools/math_stat_analysis/or.rs
@@ -141,7 +141,7 @@ impl WhiteboxTool for Or {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/math_stat_analysis/paired_sample_t_test.rs b/src/tools/math_stat_analysis/paired_sample_t_test.rs
index 4a9e0835f..2a6aa4c08 100644
--- a/src/tools/math_stat_analysis/paired_sample_t_test.rs
+++ b/src/tools/math_stat_analysis/paired_sample_t_test.rs
@@ -167,7 +167,7 @@ impl WhiteboxTool for PairedSampleTTest {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/math_stat_analysis/power.rs b/src/tools/math_stat_analysis/power.rs
index 07304839f..2716f382d 100644
--- a/src/tools/math_stat_analysis/power.rs
+++ b/src/tools/math_stat_analysis/power.rs
@@ -139,7 +139,7 @@ impl WhiteboxTool for Power {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/math_stat_analysis/principal_component_analysis.rs b/src/tools/math_stat_analysis/principal_component_analysis.rs
index 0b81ae9ee..70efe8a34 100644
--- a/src/tools/math_stat_analysis/principal_component_analysis.rs
+++ b/src/tools/math_stat_analysis/principal_component_analysis.rs
@@ -188,7 +188,7 @@ impl WhiteboxTool for PrincipalComponentAnalysis {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/math_stat_analysis/quantiles.rs b/src/tools/math_stat_analysis/quantiles.rs
index 66206fffe..c09cf657c 100644
--- a/src/tools/math_stat_analysis/quantiles.rs
+++ b/src/tools/math_stat_analysis/quantiles.rs
@@ -144,7 +144,7 @@ impl WhiteboxTool for Quantiles {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/math_stat_analysis/random_field.rs b/src/tools/math_stat_analysis/random_field.rs
index f4f693cc3..fc552481a 100644
--- a/src/tools/math_stat_analysis/random_field.rs
+++ b/src/tools/math_stat_analysis/random_field.rs
@@ -137,7 +137,7 @@ impl WhiteboxTool for RandomField {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/math_stat_analysis/random_sample.rs b/src/tools/math_stat_analysis/random_sample.rs
index 1a942f6a6..3e068fc5c 100644
--- a/src/tools/math_stat_analysis/random_sample.rs
+++ b/src/tools/math_stat_analysis/random_sample.rs
@@ -142,7 +142,7 @@ impl WhiteboxTool for RandomSample {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/math_stat_analysis/raster_histogram.rs b/src/tools/math_stat_analysis/raster_histogram.rs
index 23d8eae60..fe4611779 100644
--- a/src/tools/math_stat_analysis/raster_histogram.rs
+++ b/src/tools/math_stat_analysis/raster_histogram.rs
@@ -135,7 +135,7 @@ impl WhiteboxTool for RasterHistogram {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/math_stat_analysis/raster_summary_stats.rs b/src/tools/math_stat_analysis/raster_summary_stats.rs
index 921517c07..d9c372d81 100644
--- a/src/tools/math_stat_analysis/raster_summary_stats.rs
+++ b/src/tools/math_stat_analysis/raster_summary_stats.rs
@@ -140,7 +140,7 @@ impl WhiteboxTool for RasterSummaryStats {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/math_stat_analysis/reciprocal.rs b/src/tools/math_stat_analysis/reciprocal.rs
index 0c7c70496..fa2367d02 100644
--- a/src/tools/math_stat_analysis/reciprocal.rs
+++ b/src/tools/math_stat_analysis/reciprocal.rs
@@ -128,7 +128,7 @@ impl WhiteboxTool for Reciprocal {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/math_stat_analysis/rescale_value_range.rs b/src/tools/math_stat_analysis/rescale_value_range.rs
index 63a730e09..3f246f495 100644
--- a/src/tools/math_stat_analysis/rescale_value_range.rs
+++ b/src/tools/math_stat_analysis/rescale_value_range.rs
@@ -163,7 +163,7 @@ impl WhiteboxTool for RescaleValueRange {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/math_stat_analysis/root_mean_square_error.rs b/src/tools/math_stat_analysis/root_mean_square_error.rs
index 1dd6513ab..be2a0f81d 100644
--- a/src/tools/math_stat_analysis/root_mean_square_error.rs
+++ b/src/tools/math_stat_analysis/root_mean_square_error.rs
@@ -137,7 +137,7 @@ impl WhiteboxTool for RootMeanSquareError {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/math_stat_analysis/round.rs b/src/tools/math_stat_analysis/round.rs
index d53e01815..37629ad82 100644
--- a/src/tools/math_stat_analysis/round.rs
+++ b/src/tools/math_stat_analysis/round.rs
@@ -131,7 +131,7 @@ impl WhiteboxTool for Round {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/math_stat_analysis/sin.rs b/src/tools/math_stat_analysis/sin.rs
index 13368f491..af3609357 100644
--- a/src/tools/math_stat_analysis/sin.rs
+++ b/src/tools/math_stat_analysis/sin.rs
@@ -132,7 +132,7 @@ impl WhiteboxTool for Sin {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/math_stat_analysis/sinh.rs b/src/tools/math_stat_analysis/sinh.rs
index 40910cce5..f485b64e6 100644
--- a/src/tools/math_stat_analysis/sinh.rs
+++ b/src/tools/math_stat_analysis/sinh.rs
@@ -134,7 +134,7 @@ impl WhiteboxTool for Sinh {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/math_stat_analysis/sqrt.rs b/src/tools/math_stat_analysis/sqrt.rs
index 997c80f4a..6f6518a2a 100644
--- a/src/tools/math_stat_analysis/sqrt.rs
+++ b/src/tools/math_stat_analysis/sqrt.rs
@@ -132,7 +132,7 @@ impl WhiteboxTool for SquareRoot {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/math_stat_analysis/square.rs b/src/tools/math_stat_analysis/square.rs
index 1db4df970..a09eabd91 100644
--- a/src/tools/math_stat_analysis/square.rs
+++ b/src/tools/math_stat_analysis/square.rs
@@ -131,7 +131,7 @@ impl WhiteboxTool for Square {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/math_stat_analysis/subtract.rs b/src/tools/math_stat_analysis/subtract.rs
index 401e707a7..bbd36422c 100644
--- a/src/tools/math_stat_analysis/subtract.rs
+++ b/src/tools/math_stat_analysis/subtract.rs
@@ -141,7 +141,7 @@ impl WhiteboxTool for Subtract {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/math_stat_analysis/tan.rs b/src/tools/math_stat_analysis/tan.rs
index 2d13fb226..cbf9825a3 100644
--- a/src/tools/math_stat_analysis/tan.rs
+++ b/src/tools/math_stat_analysis/tan.rs
@@ -132,7 +132,7 @@ impl WhiteboxTool for Tan {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/math_stat_analysis/tanh.rs b/src/tools/math_stat_analysis/tanh.rs
index bb7eea19a..f53760209 100644
--- a/src/tools/math_stat_analysis/tanh.rs
+++ b/src/tools/math_stat_analysis/tanh.rs
@@ -134,7 +134,7 @@ impl WhiteboxTool for Tanh {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/math_stat_analysis/to_degrees.rs b/src/tools/math_stat_analysis/to_degrees.rs
index 9741cbba7..48dc1d3b2 100644
--- a/src/tools/math_stat_analysis/to_degrees.rs
+++ b/src/tools/math_stat_analysis/to_degrees.rs
@@ -132,7 +132,7 @@ impl WhiteboxTool for ToDegrees {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/math_stat_analysis/to_radians.rs b/src/tools/math_stat_analysis/to_radians.rs
index 10f3b9634..c29077c1e 100644
--- a/src/tools/math_stat_analysis/to_radians.rs
+++ b/src/tools/math_stat_analysis/to_radians.rs
@@ -132,7 +132,7 @@ impl WhiteboxTool for ToRadians {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/math_stat_analysis/trend_surface.rs b/src/tools/math_stat_analysis/trend_surface.rs
index 67abc0978..778ba5228 100644
--- a/src/tools/math_stat_analysis/trend_surface.rs
+++ b/src/tools/math_stat_analysis/trend_surface.rs
@@ -146,7 +146,7 @@ impl WhiteboxTool for TrendSurface {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/math_stat_analysis/trend_surface_vector_points.rs b/src/tools/math_stat_analysis/trend_surface_vector_points.rs
index ef1ff6db9..15fd11d3f 100644
--- a/src/tools/math_stat_analysis/trend_surface_vector_points.rs
+++ b/src/tools/math_stat_analysis/trend_surface_vector_points.rs
@@ -169,7 +169,7 @@ impl WhiteboxTool for TrendSurfaceVectorPoints {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/math_stat_analysis/truncate.rs b/src/tools/math_stat_analysis/truncate.rs
index 16f17f3a3..29fed2eef 100644
--- a/src/tools/math_stat_analysis/truncate.rs
+++ b/src/tools/math_stat_analysis/truncate.rs
@@ -137,7 +137,7 @@ impl WhiteboxTool for Truncate {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/math_stat_analysis/turning_bands.rs b/src/tools/math_stat_analysis/turning_bands.rs
index cff6ad1ab..ec9f8e4c9 100644
--- a/src/tools/math_stat_analysis/turning_bands.rs
+++ b/src/tools/math_stat_analysis/turning_bands.rs
@@ -173,7 +173,7 @@ impl WhiteboxTool for TurningBandsSimulation {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/math_stat_analysis/two_sample_ks_test.rs b/src/tools/math_stat_analysis/two_sample_ks_test.rs
index f92dd4fa7..b5ecb1449 100644
--- a/src/tools/math_stat_analysis/two_sample_ks_test.rs
+++ b/src/tools/math_stat_analysis/two_sample_ks_test.rs
@@ -163,7 +163,7 @@ impl WhiteboxTool for TwoSampleKsTest {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/math_stat_analysis/wilcoxon_signed_rank_test.rs b/src/tools/math_stat_analysis/wilcoxon_signed_rank_test.rs
index 911047078..5d835984b 100644
--- a/src/tools/math_stat_analysis/wilcoxon_signed_rank_test.rs
+++ b/src/tools/math_stat_analysis/wilcoxon_signed_rank_test.rs
@@ -169,7 +169,7 @@ impl WhiteboxTool for WilcoxonSignedRankTest {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
@@ -361,6 +361,8 @@ impl WhiteboxTool for WilcoxonSignedRankTest {
let mut ranks = vec![-1f64; diffs.len()];
let mut nr = 0u128; // number of non-zero ranks
let mut r = 0f64;
+ let mut r2: f64;
+ let mut upper_range: usize;
for i in 0..diffs.len() {
if diffs[i] != 0f64 {
nr += 1;
@@ -368,7 +370,7 @@ impl WhiteboxTool for WilcoxonSignedRankTest {
r += 1f64;
if i < diffs.len() - 1 {
// are there any ties above this one?
- let mut upper_range = i;
+ upper_range = i;
for j in i+1..diffs.len() {
if diffs[j].abs() == diffs[i].abs() {
upper_range = j
@@ -377,7 +379,7 @@ impl WhiteboxTool for WilcoxonSignedRankTest {
}
}
if upper_range != i {
- let r2 = r + (upper_range - i) as f64;
+ r2 = r + (upper_range - i) as f64;
r = (r + r2) / 2f64; // average rank
for k in i..=upper_range {
ranks[k] = r * diffs[k].signum();
diff --git a/src/tools/math_stat_analysis/xor.rs b/src/tools/math_stat_analysis/xor.rs
index 687c03c95..d0232ea69 100644
--- a/src/tools/math_stat_analysis/xor.rs
+++ b/src/tools/math_stat_analysis/xor.rs
@@ -141,7 +141,7 @@ impl WhiteboxTool for Xor {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/math_stat_analysis/zonal_statistics.rs b/src/tools/math_stat_analysis/zonal_statistics.rs
index 0fd4a819f..a4bbbe5d0 100644
--- a/src/tools/math_stat_analysis/zonal_statistics.rs
+++ b/src/tools/math_stat_analysis/zonal_statistics.rs
@@ -194,7 +194,7 @@ impl WhiteboxTool for ZonalStatistics {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/math_stat_analysis/zscores.rs b/src/tools/math_stat_analysis/zscores.rs
index 9710ddf1b..a0515c225 100644
--- a/src/tools/math_stat_analysis/zscores.rs
+++ b/src/tools/math_stat_analysis/zscores.rs
@@ -137,7 +137,7 @@ impl WhiteboxTool for ZScores {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/mod.rs b/src/tools/mod.rs
index 01e10c104..29ff719a8 100644
--- a/src/tools/mod.rs
+++ b/src/tools/mod.rs
@@ -106,6 +106,7 @@ impl ToolManager {
tool_names.push("MinimumBoundingEnvelope".to_string());
tool_names.push("MinimumConvexHull".to_string());
tool_names.push("NarrownessIndex".to_string());
+ tool_names.push("NaturalNeighbourInterpolation".to_string());
tool_names.push("NearestNeighbourGridding".to_string());
tool_names.push("MinOverlay".to_string());
tool_names.push("PatchOrientation".to_string());
@@ -145,6 +146,7 @@ impl ToolManager {
tool_names.push("AverageUpslopeFlowpathLength".to_string());
tool_names.push("Basins".to_string());
tool_names.push("BreachDepressions".to_string());
+ tool_names.push("BreachDepressionsLeastCost".to_string());
tool_names.push("BreachSingleCellPits".to_string());
tool_names.push("BurnStreamsAtRoads".to_string());
tool_names.push("D8FlowAccumulation".to_string());
@@ -162,6 +164,7 @@ impl ToolManager {
tool_names.push("FD8Pointer".to_string());
tool_names.push("FillBurn".to_string());
tool_names.push("FillDepressions".to_string());
+ tool_names.push("FillDepressionsWangAndLui".to_string());
tool_names.push("FillSingleCellPits".to_string());
tool_names.push("FindNoFlowCells".to_string());
tool_names.push("FindParallelFlow".to_string());
@@ -185,6 +188,7 @@ impl ToolManager {
tool_names.push("Subbasins".to_string());
tool_names.push("TraceDownslopeFlowpaths".to_string());
tool_names.push("UnnestBasins".to_string());
+ tool_names.push("UpslopeDepressionStorage".to_string());
tool_names.push("Watershed".to_string());
// image_analysis
@@ -260,6 +264,7 @@ impl ToolManager {
// tool_names.push("AsciiToLas".to_string());
tool_names.push("LidarBlockMaximum".to_string());
tool_names.push("LidarBlockMinimum".to_string());
+ tool_names.push("ClassifyBuildingsInLidar".to_string());
tool_names.push("ClassifyOverlapPoints".to_string());
tool_names.push("ClipLidarToPolygon".to_string());
tool_names.push("ErasePolygonFromLidar".to_string());
@@ -285,6 +290,7 @@ impl ToolManager {
tool_names.push("LidarNearestNeighbourGridding".to_string());
tool_names.push("LidarPointDensity".to_string());
tool_names.push("LidarPointStats".to_string());
+ tool_names.push("LidarRfbInterpolation".to_string());
tool_names.push("LidarRansacPlanes".to_string());
tool_names.push("LidarRemoveDuplicates".to_string());
tool_names.push("LidarRemoveOutliers".to_string());
@@ -330,6 +336,7 @@ impl ToolManager {
tool_names.push("GreaterThan".to_string());
tool_names.push("ImageAutocorrelation".to_string());
tool_names.push("ImageCorrelation".to_string());
+ tool_names.push("ImageCorrelationNeighbourhoodAnalysis".to_string());
tool_names.push("ImageRegression".to_string());
tool_names.push("Increment".to_string());
tool_names.push("InPlaceAdd".to_string());
@@ -576,6 +583,7 @@ impl ToolManager {
}
"minimumconvexhull" => Some(Box::new(gis_analysis::MinimumConvexHull::new())),
"minoverlay" => Some(Box::new(gis_analysis::MinOverlay::new())),
+ "naturalneighbourinterpolation" => Some(Box::new(gis_analysis::NaturalNeighbourInterpolation::new())),
"nearestneighbourgridding" => {
Some(Box::new(gis_analysis::NearestNeighbourGridding::new()))
}
@@ -623,6 +631,7 @@ impl ToolManager {
}
"basins" => Some(Box::new(hydro_analysis::Basins::new())),
"breachdepressions" => Some(Box::new(hydro_analysis::BreachDepressions::new())),
+ "breachdepressionsleastcost" => Some(Box::new(hydro_analysis::BreachDepressionsLeastCost::new())),
"breachsinglecellpits" => Some(Box::new(hydro_analysis::BreachSingleCellPits::new())),
"burnstreamsatroads" => Some(Box::new(hydro_analysis::BurnStreamsAtRoads::new())),
"d8flowaccumulation" => Some(Box::new(hydro_analysis::D8FlowAccumulation::new())),
@@ -646,6 +655,7 @@ impl ToolManager {
"fd8pointer" => Some(Box::new(hydro_analysis::FD8Pointer::new())),
"fillburn" => Some(Box::new(hydro_analysis::FillBurn::new())),
"filldepressions" => Some(Box::new(hydro_analysis::FillDepressions::new())),
+ "filldepressionswangandlui" => Some(Box::new(hydro_analysis::FillDepressionsWangAndLui::new())),
"fillsinglecellpits" => Some(Box::new(hydro_analysis::FillSingleCellPits::new())),
"findnoflowcells" => Some(Box::new(hydro_analysis::FindNoFlowCells::new())),
"findparallelflow" => Some(Box::new(hydro_analysis::FindParallelFlow::new())),
@@ -679,6 +689,7 @@ impl ToolManager {
Some(Box::new(hydro_analysis::TraceDownslopeFlowpaths::new()))
}
"unnestbasins" => Some(Box::new(hydro_analysis::UnnestBasins::new())),
+ "upslopedepressionstorage" => Some(Box::new(hydro_analysis::UpslopeDepressionStorage::new())),
"watershed" => Some(Box::new(hydro_analysis::Watershed::new())),
// image_analysis
@@ -788,6 +799,7 @@ impl ToolManager {
// "asciitolas" => Some(Box::new(lidar_analysis::AsciiToLas::new())),
"lidarblockmaximum" => Some(Box::new(lidar_analysis::LidarBlockMaximum::new())),
"lidarblockminimum" => Some(Box::new(lidar_analysis::LidarBlockMinimum::new())),
+ "classifybuildingsinlidar" => Some(Box::new(lidar_analysis::ClassifyBuildingsInLidar::new())),
"classifyoverlappoints" => Some(Box::new(lidar_analysis::ClassifyOverlapPoints::new())),
"cliplidartopolygon" => Some(Box::new(lidar_analysis::ClipLidarToPolygon::new())),
"erasepolygonfromlidar" => Some(Box::new(lidar_analysis::ErasePolygonFromLidar::new())),
@@ -823,6 +835,7 @@ impl ToolManager {
)),
"lidarpointdensity" => Some(Box::new(lidar_analysis::LidarPointDensity::new())),
"lidarpointstats" => Some(Box::new(lidar_analysis::LidarPointStats::new())),
+ "lidarrfbinterpolation" => Some(Box::new(lidar_analysis::LidarRfbInterpolation::new())),
"lidarransacplanes" => Some(Box::new(lidar_analysis::LidarRansacPlanes::new())),
"lidarremoveduplicates" => Some(Box::new(lidar_analysis::LidarRemoveDuplicates::new())),
"lidarremoveoutliers" => Some(Box::new(lidar_analysis::LidarRemoveOutliers::new())),
@@ -880,6 +893,7 @@ impl ToolManager {
Some(Box::new(math_stat_analysis::ImageAutocorrelation::new()))
}
"imagecorrelation" => Some(Box::new(math_stat_analysis::ImageCorrelation::new())),
+ "imagecorrelationneighbourhoodanalysis" => Some(Box::new(math_stat_analysis::ImageCorrelationNeighbourhoodAnalysis::new())),
"imageregression" => Some(Box::new(math_stat_analysis::ImageRegression::new())),
"increment" => Some(Box::new(math_stat_analysis::Increment::new())),
"inplaceadd" => Some(Box::new(math_stat_analysis::InPlaceAdd::new())),
diff --git a/src/tools/stream_network_analysis/dist_to_outlet.rs b/src/tools/stream_network_analysis/dist_to_outlet.rs
index 4c88f124a..44c09e68d 100644
--- a/src/tools/stream_network_analysis/dist_to_outlet.rs
+++ b/src/tools/stream_network_analysis/dist_to_outlet.rs
@@ -165,7 +165,7 @@ impl WhiteboxTool for DistanceToOutlet {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/stream_network_analysis/extract_streams.rs b/src/tools/stream_network_analysis/extract_streams.rs
index f9ab7a36f..1c505037d 100644
--- a/src/tools/stream_network_analysis/extract_streams.rs
+++ b/src/tools/stream_network_analysis/extract_streams.rs
@@ -164,7 +164,7 @@ impl WhiteboxTool for ExtractStreams {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/stream_network_analysis/extract_valleys.rs b/src/tools/stream_network_analysis/extract_valleys.rs
index a196b8f0f..4f92e30d8 100644
--- a/src/tools/stream_network_analysis/extract_valleys.rs
+++ b/src/tools/stream_network_analysis/extract_valleys.rs
@@ -207,7 +207,7 @@ impl WhiteboxTool for ExtractValleys {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/stream_network_analysis/farthest_channel_head.rs b/src/tools/stream_network_analysis/farthest_channel_head.rs
index 95b699b1a..a14f8836c 100644
--- a/src/tools/stream_network_analysis/farthest_channel_head.rs
+++ b/src/tools/stream_network_analysis/farthest_channel_head.rs
@@ -166,7 +166,7 @@ impl WhiteboxTool for FarthestChannelHead {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/stream_network_analysis/find_main_stem.rs b/src/tools/stream_network_analysis/find_main_stem.rs
index 29172ca1a..776d5e6fd 100644
--- a/src/tools/stream_network_analysis/find_main_stem.rs
+++ b/src/tools/stream_network_analysis/find_main_stem.rs
@@ -174,7 +174,7 @@ impl WhiteboxTool for FindMainStem {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/stream_network_analysis/hack_order.rs b/src/tools/stream_network_analysis/hack_order.rs
index 70f4f4fba..9bb9c8794 100644
--- a/src/tools/stream_network_analysis/hack_order.rs
+++ b/src/tools/stream_network_analysis/hack_order.rs
@@ -178,7 +178,7 @@ impl WhiteboxTool for HackStreamOrder {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/stream_network_analysis/horton_order.rs b/src/tools/stream_network_analysis/horton_order.rs
index c4b023307..35f11399c 100644
--- a/src/tools/stream_network_analysis/horton_order.rs
+++ b/src/tools/stream_network_analysis/horton_order.rs
@@ -177,7 +177,7 @@ impl WhiteboxTool for HortonStreamOrder {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/stream_network_analysis/long_profile.rs b/src/tools/stream_network_analysis/long_profile.rs
index 366caacec..57d553b90 100644
--- a/src/tools/stream_network_analysis/long_profile.rs
+++ b/src/tools/stream_network_analysis/long_profile.rs
@@ -182,7 +182,7 @@ impl WhiteboxTool for LongProfile {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/stream_network_analysis/long_profile_from_points.rs b/src/tools/stream_network_analysis/long_profile_from_points.rs
index b5f340089..3d495971d 100644
--- a/src/tools/stream_network_analysis/long_profile_from_points.rs
+++ b/src/tools/stream_network_analysis/long_profile_from_points.rs
@@ -172,7 +172,7 @@ impl WhiteboxTool for LongProfileFromPoints {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/stream_network_analysis/raster_streams_to_vector.rs b/src/tools/stream_network_analysis/raster_streams_to_vector.rs
index 7d1cbe2a0..71b8ce20f 100644
--- a/src/tools/stream_network_analysis/raster_streams_to_vector.rs
+++ b/src/tools/stream_network_analysis/raster_streams_to_vector.rs
@@ -155,7 +155,7 @@ impl WhiteboxTool for RasterStreamsToVector {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/stream_network_analysis/rasterize_streams.rs b/src/tools/stream_network_analysis/rasterize_streams.rs
index c129d1fef..dc49ed04a 100644
--- a/src/tools/stream_network_analysis/rasterize_streams.rs
+++ b/src/tools/stream_network_analysis/rasterize_streams.rs
@@ -162,7 +162,7 @@ impl WhiteboxTool for RasterizeStreams {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/stream_network_analysis/remove_short_streams.rs b/src/tools/stream_network_analysis/remove_short_streams.rs
index 04ab78ac2..9bfb65a2a 100644
--- a/src/tools/stream_network_analysis/remove_short_streams.rs
+++ b/src/tools/stream_network_analysis/remove_short_streams.rs
@@ -161,7 +161,7 @@ impl WhiteboxTool for RemoveShortStreams {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/stream_network_analysis/shreve_magnitude.rs b/src/tools/stream_network_analysis/shreve_magnitude.rs
index d33c5fe1f..4ec740f2e 100644
--- a/src/tools/stream_network_analysis/shreve_magnitude.rs
+++ b/src/tools/stream_network_analysis/shreve_magnitude.rs
@@ -170,7 +170,7 @@ impl WhiteboxTool for ShreveStreamMagnitude {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/stream_network_analysis/strahler_order.rs b/src/tools/stream_network_analysis/strahler_order.rs
index b51795b7b..4c2d609e1 100644
--- a/src/tools/stream_network_analysis/strahler_order.rs
+++ b/src/tools/stream_network_analysis/strahler_order.rs
@@ -176,7 +176,7 @@ impl WhiteboxTool for StrahlerStreamOrder {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/stream_network_analysis/stream_link_class.rs b/src/tools/stream_network_analysis/stream_link_class.rs
index 98775ffad..54c8cb14d 100644
--- a/src/tools/stream_network_analysis/stream_link_class.rs
+++ b/src/tools/stream_network_analysis/stream_link_class.rs
@@ -178,7 +178,7 @@ impl WhiteboxTool for StreamLinkClass {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/stream_network_analysis/stream_link_id.rs b/src/tools/stream_network_analysis/stream_link_id.rs
index 6be8a8e32..d4250e32b 100644
--- a/src/tools/stream_network_analysis/stream_link_id.rs
+++ b/src/tools/stream_network_analysis/stream_link_id.rs
@@ -170,7 +170,7 @@ impl WhiteboxTool for StreamLinkIdentifier {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/stream_network_analysis/stream_link_length.rs b/src/tools/stream_network_analysis/stream_link_length.rs
index 941a6ec2a..5fb6a8899 100644
--- a/src/tools/stream_network_analysis/stream_link_length.rs
+++ b/src/tools/stream_network_analysis/stream_link_length.rs
@@ -160,7 +160,7 @@ impl WhiteboxTool for StreamLinkLength {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/stream_network_analysis/stream_link_slope.rs b/src/tools/stream_network_analysis/stream_link_slope.rs
index e66abaa12..2b925fa0d 100644
--- a/src/tools/stream_network_analysis/stream_link_slope.rs
+++ b/src/tools/stream_network_analysis/stream_link_slope.rs
@@ -177,7 +177,7 @@ impl WhiteboxTool for StreamLinkSlope {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/stream_network_analysis/stream_slope_continuous.rs b/src/tools/stream_network_analysis/stream_slope_continuous.rs
index 4ef009958..ec897957e 100644
--- a/src/tools/stream_network_analysis/stream_slope_continuous.rs
+++ b/src/tools/stream_network_analysis/stream_slope_continuous.rs
@@ -178,7 +178,7 @@ impl WhiteboxTool for StreamSlopeContinuous {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/stream_network_analysis/topological_stream_order.rs b/src/tools/stream_network_analysis/topological_stream_order.rs
index 0e86ce9a5..b8d28a681 100644
--- a/src/tools/stream_network_analysis/topological_stream_order.rs
+++ b/src/tools/stream_network_analysis/topological_stream_order.rs
@@ -167,7 +167,7 @@ impl WhiteboxTool for TopologicalStreamOrder {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/stream_network_analysis/total_length_channels.rs b/src/tools/stream_network_analysis/total_length_channels.rs
index 0d3478fb7..832c92e68 100644
--- a/src/tools/stream_network_analysis/total_length_channels.rs
+++ b/src/tools/stream_network_analysis/total_length_channels.rs
@@ -164,7 +164,7 @@ impl WhiteboxTool for LengthOfUpstreamChannels {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/stream_network_analysis/tributary_id.rs b/src/tools/stream_network_analysis/tributary_id.rs
index 703597ebf..f339cd928 100644
--- a/src/tools/stream_network_analysis/tributary_id.rs
+++ b/src/tools/stream_network_analysis/tributary_id.rs
@@ -173,7 +173,7 @@ impl WhiteboxTool for TributaryIdentifier {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/stream_network_analysis/vector_stream_network_analysis.rs b/src/tools/stream_network_analysis/vector_stream_network_analysis.rs
index 3f93e5dc9..721d0a432 100644
--- a/src/tools/stream_network_analysis/vector_stream_network_analysis.rs
+++ b/src/tools/stream_network_analysis/vector_stream_network_analysis.rs
@@ -155,7 +155,7 @@ impl WhiteboxTool for VectorStreamNetworkAnalysis {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/terrain_analysis/aspect.rs b/src/tools/terrain_analysis/aspect.rs
index c88eadbc1..5bef3f035 100644
--- a/src/tools/terrain_analysis/aspect.rs
+++ b/src/tools/terrain_analysis/aspect.rs
@@ -167,7 +167,7 @@ impl WhiteboxTool for Aspect {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/terrain_analysis/average_normal_vector_angular_deviation.rs b/src/tools/terrain_analysis/average_normal_vector_angular_deviation.rs
index 9e609ffbb..8e2e8db79 100644
--- a/src/tools/terrain_analysis/average_normal_vector_angular_deviation.rs
+++ b/src/tools/terrain_analysis/average_normal_vector_angular_deviation.rs
@@ -152,7 +152,7 @@ impl WhiteboxTool for AverageNormalVectorAngularDeviation {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/terrain_analysis/circular_variance_of_aspect.rs b/src/tools/terrain_analysis/circular_variance_of_aspect.rs
index cd5a23f40..b34545fc5 100644
--- a/src/tools/terrain_analysis/circular_variance_of_aspect.rs
+++ b/src/tools/terrain_analysis/circular_variance_of_aspect.rs
@@ -159,7 +159,7 @@ impl WhiteboxTool for CircularVarianceOfAspect {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/terrain_analysis/dev_from_mean_elev.rs b/src/tools/terrain_analysis/dev_from_mean_elev.rs
index 289fd67dc..90ab560ab 100644
--- a/src/tools/terrain_analysis/dev_from_mean_elev.rs
+++ b/src/tools/terrain_analysis/dev_from_mean_elev.rs
@@ -164,7 +164,7 @@ impl WhiteboxTool for DevFromMeanElev {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/terrain_analysis/diff_from_mean_elev.rs b/src/tools/terrain_analysis/diff_from_mean_elev.rs
index a5a3ba171..fcd487b06 100644
--- a/src/tools/terrain_analysis/diff_from_mean_elev.rs
+++ b/src/tools/terrain_analysis/diff_from_mean_elev.rs
@@ -165,7 +165,7 @@ impl WhiteboxTool for DiffFromMeanElev {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/terrain_analysis/directional_relief.rs b/src/tools/terrain_analysis/directional_relief.rs
index 68dee1959..4a10f89a6 100644
--- a/src/tools/terrain_analysis/directional_relief.rs
+++ b/src/tools/terrain_analysis/directional_relief.rs
@@ -171,7 +171,7 @@ impl WhiteboxTool for DirectionalRelief {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/terrain_analysis/downslope_index.rs b/src/tools/terrain_analysis/downslope_index.rs
index e94f3fd80..6555547e9 100644
--- a/src/tools/terrain_analysis/downslope_index.rs
+++ b/src/tools/terrain_analysis/downslope_index.rs
@@ -172,7 +172,7 @@ impl WhiteboxTool for DownslopeIndex {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/terrain_analysis/drainage_preserving_smoothing.rs b/src/tools/terrain_analysis/drainage_preserving_smoothing.rs
index fd622a6b4..5fdcdcd70 100644
--- a/src/tools/terrain_analysis/drainage_preserving_smoothing.rs
+++ b/src/tools/terrain_analysis/drainage_preserving_smoothing.rs
@@ -219,7 +219,7 @@ impl WhiteboxTool for DrainagePreservingSmoothing {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/terrain_analysis/edge_density.rs b/src/tools/terrain_analysis/edge_density.rs
index 24cdd97c4..168ec736d 100644
--- a/src/tools/terrain_analysis/edge_density.rs
+++ b/src/tools/terrain_analysis/edge_density.rs
@@ -174,7 +174,7 @@ impl WhiteboxTool for EdgeDensity {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/terrain_analysis/elev_above_pit.rs b/src/tools/terrain_analysis/elev_above_pit.rs
index 6f6af2da8..b15aee906 100644
--- a/src/tools/terrain_analysis/elev_above_pit.rs
+++ b/src/tools/terrain_analysis/elev_above_pit.rs
@@ -134,7 +134,7 @@ impl WhiteboxTool for ElevAbovePit {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/terrain_analysis/elev_percentile.rs b/src/tools/terrain_analysis/elev_percentile.rs
index fb1b022ed..45a92e18b 100644
--- a/src/tools/terrain_analysis/elev_percentile.rs
+++ b/src/tools/terrain_analysis/elev_percentile.rs
@@ -195,7 +195,7 @@ impl WhiteboxTool for ElevPercentile {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/terrain_analysis/elev_relative_to_min_max.rs b/src/tools/terrain_analysis/elev_relative_to_min_max.rs
index c88564ed3..04d6eaad1 100644
--- a/src/tools/terrain_analysis/elev_relative_to_min_max.rs
+++ b/src/tools/terrain_analysis/elev_relative_to_min_max.rs
@@ -131,7 +131,7 @@ impl WhiteboxTool for ElevRelativeToMinMax {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/terrain_analysis/elev_relative_to_watershed_min_max.rs b/src/tools/terrain_analysis/elev_relative_to_watershed_min_max.rs
index be4af186b..a340969a1 100644
--- a/src/tools/terrain_analysis/elev_relative_to_watershed_min_max.rs
+++ b/src/tools/terrain_analysis/elev_relative_to_watershed_min_max.rs
@@ -138,7 +138,7 @@ impl WhiteboxTool for ElevRelativeToWatershedMinMax {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/terrain_analysis/feature_preserving_smoothing.rs b/src/tools/terrain_analysis/feature_preserving_smoothing.rs
index b21737c57..b75ec2166 100644
--- a/src/tools/terrain_analysis/feature_preserving_smoothing.rs
+++ b/src/tools/terrain_analysis/feature_preserving_smoothing.rs
@@ -206,7 +206,7 @@ impl WhiteboxTool for FeaturePreservingSmoothing {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/terrain_analysis/fetch_analysis.rs b/src/tools/terrain_analysis/fetch_analysis.rs
index 7af4d806a..3379cc06d 100644
--- a/src/tools/terrain_analysis/fetch_analysis.rs
+++ b/src/tools/terrain_analysis/fetch_analysis.rs
@@ -173,7 +173,7 @@ impl WhiteboxTool for FetchAnalysis {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/terrain_analysis/fill_missing_data.rs b/src/tools/terrain_analysis/fill_missing_data.rs
index 44b8b92c5..746d99dcc 100644
--- a/src/tools/terrain_analysis/fill_missing_data.rs
+++ b/src/tools/terrain_analysis/fill_missing_data.rs
@@ -159,7 +159,7 @@ impl WhiteboxTool for FillMissingData {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/terrain_analysis/find_ridges.rs b/src/tools/terrain_analysis/find_ridges.rs
index ebece8c78..82645be06 100644
--- a/src/tools/terrain_analysis/find_ridges.rs
+++ b/src/tools/terrain_analysis/find_ridges.rs
@@ -138,7 +138,7 @@ impl WhiteboxTool for FindRidges {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/terrain_analysis/geomorphons.rs b/src/tools/terrain_analysis/geomorphons.rs
index 74e650a6f..2ea304614 100644
--- a/src/tools/terrain_analysis/geomorphons.rs
+++ b/src/tools/terrain_analysis/geomorphons.rs
@@ -153,7 +153,7 @@ impl WhiteboxTool for Geomorphons {
if args.len() == 0 {
return Err(Error::new(ErrorKind::InvalidInput,
- "Tool run with no paramters."));
+ "Tool run with no parameters."));
}
for i in 0..args.len() {
let mut arg = args[i].replace("\"", "");
diff --git a/src/tools/terrain_analysis/hillshade.rs b/src/tools/terrain_analysis/hillshade.rs
index f044fe8dc..2f44539d8 100644
--- a/src/tools/terrain_analysis/hillshade.rs
+++ b/src/tools/terrain_analysis/hillshade.rs
@@ -182,7 +182,7 @@ impl WhiteboxTool for Hillshade {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/terrain_analysis/horizon_angle.rs b/src/tools/terrain_analysis/horizon_angle.rs
index 62312d956..5a1152bcd 100644
--- a/src/tools/terrain_analysis/horizon_angle.rs
+++ b/src/tools/terrain_analysis/horizon_angle.rs
@@ -168,7 +168,7 @@ impl WhiteboxTool for HorizonAngle {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/terrain_analysis/hypsometric_analysis.rs b/src/tools/terrain_analysis/hypsometric_analysis.rs
index 2afadd1d6..c31621d29 100644
--- a/src/tools/terrain_analysis/hypsometric_analysis.rs
+++ b/src/tools/terrain_analysis/hypsometric_analysis.rs
@@ -142,7 +142,7 @@ impl WhiteboxTool for HypsometricAnalysis {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/terrain_analysis/max_anisotropy_dev.rs b/src/tools/terrain_analysis/max_anisotropy_dev.rs
index a81b0eba1..fd51bc219 100644
--- a/src/tools/terrain_analysis/max_anisotropy_dev.rs
+++ b/src/tools/terrain_analysis/max_anisotropy_dev.rs
@@ -162,7 +162,7 @@ impl WhiteboxTool for MaxAnisotropyDev {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/terrain_analysis/max_anisotropy_dev_signature.rs b/src/tools/terrain_analysis/max_anisotropy_dev_signature.rs
index dcd4a51f9..178e63165 100644
--- a/src/tools/terrain_analysis/max_anisotropy_dev_signature.rs
+++ b/src/tools/terrain_analysis/max_anisotropy_dev_signature.rs
@@ -166,7 +166,7 @@ impl WhiteboxTool for MaxAnisotropyDevSignature {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/terrain_analysis/max_branch_length.rs b/src/tools/terrain_analysis/max_branch_length.rs
index d9ebe2163..17d41e6b5 100644
--- a/src/tools/terrain_analysis/max_branch_length.rs
+++ b/src/tools/terrain_analysis/max_branch_length.rs
@@ -167,7 +167,7 @@ impl WhiteboxTool for MaxBranchLength {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/terrain_analysis/max_diff_from_mean.rs b/src/tools/terrain_analysis/max_diff_from_mean.rs
index f862d83d4..d34d8626c 100644
--- a/src/tools/terrain_analysis/max_diff_from_mean.rs
+++ b/src/tools/terrain_analysis/max_diff_from_mean.rs
@@ -164,7 +164,7 @@ impl WhiteboxTool for MaxDifferenceFromMean {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/terrain_analysis/max_downslope_elev_change.rs b/src/tools/terrain_analysis/max_downslope_elev_change.rs
index eed470aea..375ebd54d 100644
--- a/src/tools/terrain_analysis/max_downslope_elev_change.rs
+++ b/src/tools/terrain_analysis/max_downslope_elev_change.rs
@@ -132,7 +132,7 @@ impl WhiteboxTool for MaxDownslopeElevChange {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/terrain_analysis/max_elev_dev_signature.rs b/src/tools/terrain_analysis/max_elev_dev_signature.rs
index ddee10890..c3e9bb9a7 100644
--- a/src/tools/terrain_analysis/max_elev_dev_signature.rs
+++ b/src/tools/terrain_analysis/max_elev_dev_signature.rs
@@ -166,7 +166,7 @@ impl WhiteboxTool for MaxElevDevSignature {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/terrain_analysis/max_elev_deviation.rs b/src/tools/terrain_analysis/max_elev_deviation.rs
index d0c90f1ce..e6fb50741 100644
--- a/src/tools/terrain_analysis/max_elev_deviation.rs
+++ b/src/tools/terrain_analysis/max_elev_deviation.rs
@@ -198,7 +198,7 @@ impl WhiteboxTool for MaxElevationDeviation {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/terrain_analysis/min_downslope_elev_change.rs b/src/tools/terrain_analysis/min_downslope_elev_change.rs
index 19806c4fc..960fb81d5 100644
--- a/src/tools/terrain_analysis/min_downslope_elev_change.rs
+++ b/src/tools/terrain_analysis/min_downslope_elev_change.rs
@@ -132,7 +132,7 @@ impl WhiteboxTool for MinDownslopeElevChange {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/terrain_analysis/multiscale_roughness.rs b/src/tools/terrain_analysis/multiscale_roughness.rs
index d8646d864..4c0ff42bc 100644
--- a/src/tools/terrain_analysis/multiscale_roughness.rs
+++ b/src/tools/terrain_analysis/multiscale_roughness.rs
@@ -164,7 +164,7 @@ impl WhiteboxTool for MultiscaleRoughness {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/terrain_analysis/multiscale_roughness_signature.rs b/src/tools/terrain_analysis/multiscale_roughness_signature.rs
index 65becc39d..4bcb21135 100644
--- a/src/tools/terrain_analysis/multiscale_roughness_signature.rs
+++ b/src/tools/terrain_analysis/multiscale_roughness_signature.rs
@@ -174,7 +174,7 @@ impl WhiteboxTool for MultiscaleRoughnessSignature {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/terrain_analysis/multiscale_std_dev_normals.rs b/src/tools/terrain_analysis/multiscale_std_dev_normals.rs
index f16664ea5..a3beb5b27 100644
--- a/src/tools/terrain_analysis/multiscale_std_dev_normals.rs
+++ b/src/tools/terrain_analysis/multiscale_std_dev_normals.rs
@@ -212,7 +212,7 @@ impl WhiteboxTool for MultiscaleStdDevNormals {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/terrain_analysis/multiscale_std_dev_normals_signature.rs b/src/tools/terrain_analysis/multiscale_std_dev_normals_signature.rs
index ac2057038..92bbf817e 100644
--- a/src/tools/terrain_analysis/multiscale_std_dev_normals_signature.rs
+++ b/src/tools/terrain_analysis/multiscale_std_dev_normals_signature.rs
@@ -182,7 +182,7 @@ impl WhiteboxTool for MultiscaleStdDevNormalsSignature {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/terrain_analysis/multiscale_topographic_position_image.rs b/src/tools/terrain_analysis/multiscale_topographic_position_image.rs
index d741a413d..6a905438b 100644
--- a/src/tools/terrain_analysis/multiscale_topographic_position_image.rs
+++ b/src/tools/terrain_analysis/multiscale_topographic_position_image.rs
@@ -173,7 +173,7 @@ impl WhiteboxTool for MultiscaleTopographicPositionImage {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/terrain_analysis/num_downslope_neighbours.rs b/src/tools/terrain_analysis/num_downslope_neighbours.rs
index 8cfd22088..9c52e19b3 100644
--- a/src/tools/terrain_analysis/num_downslope_neighbours.rs
+++ b/src/tools/terrain_analysis/num_downslope_neighbours.rs
@@ -135,7 +135,7 @@ impl WhiteboxTool for NumDownslopeNeighbours {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/terrain_analysis/num_upslope_neighbours.rs b/src/tools/terrain_analysis/num_upslope_neighbours.rs
index d6073be78..1ce05d4f9 100644
--- a/src/tools/terrain_analysis/num_upslope_neighbours.rs
+++ b/src/tools/terrain_analysis/num_upslope_neighbours.rs
@@ -136,7 +136,7 @@ impl WhiteboxTool for NumUpslopeNeighbours {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/terrain_analysis/pennock_landform_class.rs b/src/tools/terrain_analysis/pennock_landform_class.rs
index f214a1ad0..a94e94250 100644
--- a/src/tools/terrain_analysis/pennock_landform_class.rs
+++ b/src/tools/terrain_analysis/pennock_landform_class.rs
@@ -213,7 +213,7 @@ impl WhiteboxTool for PennockLandformClass {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/terrain_analysis/percent_elev_range.rs b/src/tools/terrain_analysis/percent_elev_range.rs
index 1446d9c05..cba3dd662 100644
--- a/src/tools/terrain_analysis/percent_elev_range.rs
+++ b/src/tools/terrain_analysis/percent_elev_range.rs
@@ -166,7 +166,7 @@ impl WhiteboxTool for PercentElevRange {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/terrain_analysis/plan_curvature.rs b/src/tools/terrain_analysis/plan_curvature.rs
index 2d59148de..01ba62302 100644
--- a/src/tools/terrain_analysis/plan_curvature.rs
+++ b/src/tools/terrain_analysis/plan_curvature.rs
@@ -163,7 +163,7 @@ impl WhiteboxTool for PlanCurvature {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/terrain_analysis/prof_curvature.rs b/src/tools/terrain_analysis/prof_curvature.rs
index 1a2498ec5..d1d5eaebb 100644
--- a/src/tools/terrain_analysis/prof_curvature.rs
+++ b/src/tools/terrain_analysis/prof_curvature.rs
@@ -163,7 +163,7 @@ impl WhiteboxTool for ProfileCurvature {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/terrain_analysis/profile.rs b/src/tools/terrain_analysis/profile.rs
index b5761a492..9ea0b4a30 100644
--- a/src/tools/terrain_analysis/profile.rs
+++ b/src/tools/terrain_analysis/profile.rs
@@ -147,7 +147,7 @@ impl WhiteboxTool for Profile {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/terrain_analysis/relative_aspect.rs b/src/tools/terrain_analysis/relative_aspect.rs
index d306e91f6..d8e0b0f58 100644
--- a/src/tools/terrain_analysis/relative_aspect.rs
+++ b/src/tools/terrain_analysis/relative_aspect.rs
@@ -164,7 +164,7 @@ impl WhiteboxTool for RelativeAspect {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/terrain_analysis/relative_stream_power_index.rs b/src/tools/terrain_analysis/relative_stream_power_index.rs
index 257762c0b..500c3fb77 100644
--- a/src/tools/terrain_analysis/relative_stream_power_index.rs
+++ b/src/tools/terrain_analysis/relative_stream_power_index.rs
@@ -162,7 +162,7 @@ impl WhiteboxTool for StreamPowerIndex {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/terrain_analysis/relative_topographic_position.rs b/src/tools/terrain_analysis/relative_topographic_position.rs
index d37f50891..d7cc705a6 100644
--- a/src/tools/terrain_analysis/relative_topographic_position.rs
+++ b/src/tools/terrain_analysis/relative_topographic_position.rs
@@ -175,7 +175,7 @@ impl WhiteboxTool for RelativeTopographicPosition {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/terrain_analysis/remove_off_terrain_objects.rs b/src/tools/terrain_analysis/remove_off_terrain_objects.rs
index 2a6ec67f4..a5c92ae29 100644
--- a/src/tools/terrain_analysis/remove_off_terrain_objects.rs
+++ b/src/tools/terrain_analysis/remove_off_terrain_objects.rs
@@ -161,7 +161,7 @@ impl WhiteboxTool for RemoveOffTerrainObjects {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/terrain_analysis/ruggedness_index.rs b/src/tools/terrain_analysis/ruggedness_index.rs
index 92646bc63..c3d7ab100 100644
--- a/src/tools/terrain_analysis/ruggedness_index.rs
+++ b/src/tools/terrain_analysis/ruggedness_index.rs
@@ -155,7 +155,7 @@ impl WhiteboxTool for RuggednessIndex {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/terrain_analysis/sediment_transport_index.rs b/src/tools/terrain_analysis/sediment_transport_index.rs
index 97e63bf61..7b61e22ee 100644
--- a/src/tools/terrain_analysis/sediment_transport_index.rs
+++ b/src/tools/terrain_analysis/sediment_transport_index.rs
@@ -183,7 +183,7 @@ impl WhiteboxTool for SedimentTransportIndex {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
@@ -313,7 +313,7 @@ impl WhiteboxTool for SedimentTransportIndex {
}
}
- let elapsed_time = get_formatted_elapsed_time(start);;
+ let elapsed_time = get_formatted_elapsed_time(start);
output.configs.data_type = DataType::F32;
output.configs.palette = "grey.plt".to_string();
output.configs.photometric_interp = PhotometricInterpretation::Continuous;
diff --git a/src/tools/terrain_analysis/slope.rs b/src/tools/terrain_analysis/slope.rs
index 4c1a0a730..0d627792a 100644
--- a/src/tools/terrain_analysis/slope.rs
+++ b/src/tools/terrain_analysis/slope.rs
@@ -174,7 +174,7 @@ impl WhiteboxTool for Slope {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/terrain_analysis/slope_vs_elev_plot.rs b/src/tools/terrain_analysis/slope_vs_elev_plot.rs
index bd4c9b547..108fd8cd6 100644
--- a/src/tools/terrain_analysis/slope_vs_elev_plot.rs
+++ b/src/tools/terrain_analysis/slope_vs_elev_plot.rs
@@ -154,7 +154,7 @@ impl WhiteboxTool for SlopeVsElevationPlot {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
@@ -601,7 +601,7 @@ impl WhiteboxTool for SlopeVsElevationPlot {
}
}
writer.write_all(("
").as_bytes())?;
- let elapsed_time = get_formatted_elapsed_time(start);;
+ let elapsed_time = get_formatted_elapsed_time(start);
let multiples = num_files > 1;
diff --git a/src/tools/terrain_analysis/spherical_std_dev_of_normals.rs b/src/tools/terrain_analysis/spherical_std_dev_of_normals.rs
index ece0b7981..32d7a2eaf 100644
--- a/src/tools/terrain_analysis/spherical_std_dev_of_normals.rs
+++ b/src/tools/terrain_analysis/spherical_std_dev_of_normals.rs
@@ -169,7 +169,7 @@ impl WhiteboxTool for SphericalStdDevOfNormals {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/terrain_analysis/standard_deviation_of_slope.rs b/src/tools/terrain_analysis/standard_deviation_of_slope.rs
index 3b90140f3..f539cce2f 100644
--- a/src/tools/terrain_analysis/standard_deviation_of_slope.rs
+++ b/src/tools/terrain_analysis/standard_deviation_of_slope.rs
@@ -162,7 +162,7 @@ impl WhiteboxTool for StandardDeviationOfSlope {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
//checks arguments
diff --git a/src/tools/terrain_analysis/surface_area_ratio.rs b/src/tools/terrain_analysis/surface_area_ratio.rs
index 63ee33e62..48873ab94 100644
--- a/src/tools/terrain_analysis/surface_area_ratio.rs
+++ b/src/tools/terrain_analysis/surface_area_ratio.rs
@@ -137,7 +137,7 @@ impl WhiteboxTool for SurfaceAreaRatio {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/terrain_analysis/tan_curvature.rs b/src/tools/terrain_analysis/tan_curvature.rs
index 0147ae713..c14bb671f 100644
--- a/src/tools/terrain_analysis/tan_curvature.rs
+++ b/src/tools/terrain_analysis/tan_curvature.rs
@@ -153,7 +153,7 @@ impl WhiteboxTool for TangentialCurvature {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/terrain_analysis/total_curvature.rs b/src/tools/terrain_analysis/total_curvature.rs
index 61edf5e5a..9213746b0 100644
--- a/src/tools/terrain_analysis/total_curvature.rs
+++ b/src/tools/terrain_analysis/total_curvature.rs
@@ -155,7 +155,7 @@ impl WhiteboxTool for TotalCurvature {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/terrain_analysis/viewshed.rs b/src/tools/terrain_analysis/viewshed.rs
index cb2e4436e..2ce7b24ff 100644
--- a/src/tools/terrain_analysis/viewshed.rs
+++ b/src/tools/terrain_analysis/viewshed.rs
@@ -169,7 +169,7 @@ impl WhiteboxTool for Viewshed {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/terrain_analysis/visibility_index.rs b/src/tools/terrain_analysis/visibility_index.rs
index 338a5ab2e..b3c8fe5fa 100644
--- a/src/tools/terrain_analysis/visibility_index.rs
+++ b/src/tools/terrain_analysis/visibility_index.rs
@@ -166,7 +166,7 @@ impl WhiteboxTool for VisibilityIndex {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/tools/terrain_analysis/wetness_index.rs b/src/tools/terrain_analysis/wetness_index.rs
index 5a68322f6..192e1baf8 100644
--- a/src/tools/terrain_analysis/wetness_index.rs
+++ b/src/tools/terrain_analysis/wetness_index.rs
@@ -132,7 +132,7 @@ impl WhiteboxTool for WetnessIndex {
if args.len() == 0 {
return Err(Error::new(
ErrorKind::InvalidInput,
- "Tool run with no paramters.",
+ "Tool run with no parameters.",
));
}
for i in 0..args.len() {
diff --git a/src/vector/shapefile/mod.rs b/src/vector/shapefile/mod.rs
index b6e611675..f6da15c62 100644
--- a/src/vector/shapefile/mod.rs
+++ b/src/vector/shapefile/mod.rs
@@ -1,7 +1,7 @@
/*
This code is part of the WhiteboxTools geospatial analysis library.
Authors: Dr. John Lindsay
-Created: June 21, 2017
+Created: 21/06/2017
Last Modified: 17/10/2018
License: MIT
@@ -13,15 +13,10 @@ pub mod geometry;
use self::attributes::*;
use self::geometry::*;
-// use attributes::{
-// AttributeField, AttributeHeader, DateData, FieldData, FieldDataType, Intersector,
-// ShapefileAttributes,
-// };
use crate::structures::Point2D;
use crate::utils::{ByteOrderReader, Endianness};
use byteorder::{BigEndian, LittleEndian, WriteBytesExt};
use chrono::prelude::*;
-// use geometry::{ShapeType, ShapeTypeDimension, ShapefileGeometry};
use std::f64;
use std::fmt;
use std::fs;
@@ -31,7 +26,6 @@ use std::io::{BufReader, BufWriter, Cursor, Error, ErrorKind};
use std::path::Path;
use std::str;
-/// `ShapefileHeader` stores the header variables of a ShapeFile header.
#[derive(Debug, Default, Clone)]
pub struct ShapefileHeader {
file_code: i32, // BigEndian; value is 9994
diff --git a/tempCodeRunnerFile.py b/tempCodeRunnerFile.py
deleted file mode 100644
index 414732016..000000000
--- a/tempCodeRunnerFile.py
+++ /dev/null
@@ -1,7321 +0,0 @@
-#!/usr/bin/env python
-''' This file is intended to be a helper for running whitebox-tools plugins from a Python script.
-See whitebox_example.py for an example of how to use it.
-'''
-
-# This script is part of the WhiteboxTools geospatial library.
-# Authors: Dr. John Lindsay
-# Created: 28/11/2017
-# Last Modified: 22/04/2018
-# License: MIT
-
-from __future__ import print_function
-import os
-from os import path
-import sys
-import platform
-import re
-# import shutil
-from subprocess import CalledProcessError, Popen, PIPE, STDOUT
-
-
-def default_callback(value):
- '''
- A simple default callback that outputs using the print function. When
- tools are called without providing a custom callback, this function
- will be used to print to standard output.
- '''
- print(value)
-
-
-def to_camelcase(name):
- '''
- Convert snake_case name to CamelCase name
- '''
- return ''.join(x.title() for x in name.split('_'))
-
-
-def to_snakecase(name):
- '''
- Convert CamelCase name to snake_case name
- '''
- s1 = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', name)
- return re.sub('([a-z0-9])([A-Z])', r'\1_\2', s1).lower()
-
-
-class WhiteboxTools(object):
- '''
- An object for interfacing with the WhiteboxTools executable.
- '''
-
- def __init__(self):
- if platform.system() == 'Windows':
- self.ext = '.exe'
- else:
- self.ext = ''
- self.exe_name = "whitebox_tools{}".format(self.ext)
- # self.exe_path = os.path.dirname(shutil.which(
- # self.exe_name) or path.dirname(path.abspath(__file__)))
- # self.exe_path = os.path.dirname(os.path.join(os.path.realpath(__file__)))
- self.exe_path = path.dirname(path.abspath(__file__))
- self.work_dir = ""
- self.verbose = True
- self.cancel_op = False
- self.default_callback = default_callback
-
- def set_whitebox_dir(self, path_str):
- '''
- Sets the directory to the WhiteboxTools executable file.
- '''
- self.exe_path = path_str
-
- def set_working_dir(self, path_str):
- '''
- Sets the working directory, i.e. the directory in which
- the data files are located. By setting the working
- directory, tool input parameters that are files need only
- specify the file name rather than the complete file path.
- '''
- self.work_dir = path.normpath(path_str)
-
- def set_verbose_mode(self, val=True):
- '''
- Sets verbose mode. If verbose mode is False, tools will not
- print output messages. Tools will frequently provide substantial
- feedback while they are operating, e.g. updating progress for
- various sub-routines. When the user has scripted a workflow
- that ties many tools in sequence, this level of tool output
- can be problematic. By setting verbose mode to False, these
- messages are suppressed and tools run as background processes.
- '''
- self.verbose = val
-
- def run_tool(self, tool_name, args, callback=None):
- '''
- Runs a tool and specifies tool arguments.
- Returns 0 if completes without error.
- Returns 1 if error encountered (details are sent to callback).
- Returns 2 if process is cancelled by user.
- '''
- try:
- if callback is None:
- callback = self.default_callback
-
- os.chdir(self.exe_path)
- args2 = []
- args2.append("." + path.sep + self.exe_name)
- args2.append("--run=\"{}\"".format(to_camelcase(tool_name)))
-
- if self.work_dir.strip() != "":
- args2.append("--wd=\"{}\"".format(self.work_dir))
-
- for arg in args:
- args2.append(arg)
-
- # args_str = args_str[:-1]
- # a.append("--args=\"{}\"".format(args_str))
-
- if self.verbose:
- args2.append("-v")
-
- if self.verbose:
- cl = ""
- for v in args2:
- cl += v + " "
- callback(cl.strip() + "\n")
-
- proc = Popen(args2, shell=False, stdout=PIPE,
- stderr=STDOUT, bufsize=1, universal_newlines=True)
-
- while True:
- line = proc.stdout.readline()
- sys.stdout.flush()
- if line != '':
- if not self.cancel_op:
- callback(line.strip())
- else:
- self.cancel_op = False
- proc.terminate()
- return 2
-
- else:
- break
-
- return 0
- except (OSError, ValueError, CalledProcessError) as err:
- callback(str(err))
- return 1
-
- def help(self):
- '''
- Retrieves the help description for WhiteboxTools.
- '''
- try:
- os.chdir(self.exe_path)
- args = []
- args.append("." + os.path.sep + self.exe_name)
- args.append("-h")
-
- proc = Popen(args, shell=False, stdout=PIPE,
- stderr=STDOUT, bufsize=1, universal_newlines=True)
- ret = ""
- while True:
- line = proc.stdout.readline()
- if line != '':
- ret += line
- else:
- break
-
- return ret
- except (OSError, ValueError, CalledProcessError) as err:
- return err
-
- def license(self):
- '''
- Retrieves the license information for WhiteboxTools.
- '''
- try:
- os.chdir(self.exe_path)
- args = []
- args.append("." + os.path.sep + self.exe_name)
- args.append("--license")
-
- proc = Popen(args, shell=False, stdout=PIPE,
- stderr=STDOUT, bufsize=1, universal_newlines=True)
- ret = ""
- while True:
- line = proc.stdout.readline()
- if line != '':
- ret += line
- else:
- break
-
- return ret
- except (OSError, ValueError, CalledProcessError) as err:
- return err
-
- def version(self):
- '''
- Retrieves the version information for WhiteboxTools.
- '''
- try:
- os.chdir(self.exe_path)
- args = []
- args.append("." + os.path.sep + self.exe_name)
- args.append("--version")
-
- proc = Popen(args, shell=False, stdout=PIPE,
- stderr=STDOUT, bufsize=1, universal_newlines=True)
- ret = ""
- while True:
- line = proc.stdout.readline()
- if line != '':
- ret += line
- else:
- break
-
- return ret
- except (OSError, ValueError, CalledProcessError) as err:
- return err
-
- def tool_help(self, tool_name=''):
- '''
- Retrieves the help description for a specific tool.
- '''
- try:
- os.chdir(self.exe_path)
- args = []
- args.append("." + os.path.sep + self.exe_name)
- args.append("--toolhelp={}".format(to_camelcase(tool_name)))
-
- proc = Popen(args, shell=False, stdout=PIPE,
- stderr=STDOUT, bufsize=1, universal_newlines=True)
- ret = ""
- while True:
- line = proc.stdout.readline()
- if line != '':
- ret += line
- else:
- break
-
- return ret
- except (OSError, ValueError, CalledProcessError) as err:
- return err
-
- def tool_parameters(self, tool_name):
- '''
- Retrieves the tool parameter descriptions for a specific tool.
- '''
- try:
- os.chdir(self.exe_path)
- args = []
- args.append("." + os.path.sep + self.exe_name)
- args.append("--toolparameters={}".format(to_camelcase(tool_name)))
-
- proc = Popen(args, shell=False, stdout=PIPE,
- stderr=STDOUT, bufsize=1, universal_newlines=True)
- ret = ""
- while True:
- line = proc.stdout.readline()
- if line != '':
- ret += line
- else:
- break
-
- return ret
- except (OSError, ValueError, CalledProcessError) as err:
- return err
-
- def toolbox(self, tool_name=''):
- '''
- Retrieve the toolbox for a specific tool.
- '''
- try:
- os.chdir(self.exe_path)
- args = []
- args.append("." + os.path.sep + self.exe_name)
- args.append("--toolbox={}".format(to_camelcase(tool_name)))
-
- proc = Popen(args, shell=False, stdout=PIPE,
- stderr=STDOUT, bufsize=1, universal_newlines=True)
- ret = ""
- while True:
- line = proc.stdout.readline()
- if line != '':
- ret += line
- else:
- break
-
- return ret
- except (OSError, ValueError, CalledProcessError) as err:
- return err
-
- def view_code(self, tool_name):
- '''
- Opens a web browser to view the source code for a specific tool
- on the projects source code repository.
- '''
- try:
- os.chdir(self.exe_path)
- args = []
- args.append("." + os.path.sep + self.exe_name)
- args.append("--viewcode={}".format(to_camelcase(tool_name)))
-
- proc = Popen(args, shell=False, stdout=PIPE,
- stderr=STDOUT, bufsize=1, universal_newlines=True)
- ret = ""
- while True:
- line = proc.stdout.readline()
- if line != '':
- ret += line
- else:
- break
-
- return ret
- except (OSError, ValueError, CalledProcessError) as err:
- return err
-
- def list_tools(self, keywords=[]):
- '''
- Lists all available tools in WhiteboxTools.
- '''
- try:
- os.chdir(self.exe_path)
- args = []
- args.append("." + os.path.sep + self.exe_name)
- args.append("--listtools")
- if len(keywords) > 0:
- for kw in keywords:
- args.append(kw)
-
- proc = Popen(args, shell=False, stdout=PIPE,
- stderr=STDOUT, bufsize=1, universal_newlines=True)
- ret = {}
- line = proc.stdout.readline() # skip number of available tools header
- while True:
- line = proc.stdout.readline()
- if line != '':
- if line.strip() != '':
- name, descr = line.split(':')
- ret[to_snakecase(name.strip())] = descr.strip()
- else:
- break
-
- return ret
- except (OSError, ValueError, CalledProcessError) as err:
- return err
-
- ########################################################################
- # The following methods are convenience methods for each available tool.
- # This needs updating whenever new tools are added to the WhiteboxTools
- # library. They can be generated automatically using the
- # whitebox_plugin_generator.py script. It would also be possible to
- # discover plugins at runtime and monkey-patch their methods using
- # MethodType. However, this would not be as useful since it would
- # restrict the ability for text editors and IDEs to use autocomplete.
- ########################################################################
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- ##############
- # Data Tools #
- ##############
-
- def add_point_coordinates_to_table(self, i, callback=None):
- """Modifies the attribute table of a point vector by adding fields containing each point's X and Y coordinates.
-
- Keyword arguments:
-
- i -- Input vector Points file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- return self.run_tool('add_point_coordinates_to_table', args, callback) # returns 1 if error
-
- def convert_nodata_to_zero(self, i, output, callback=None):
- """Converts nodata values in a raster to zero.
-
- Keyword arguments:
-
- i -- Input raster file.
- output -- Output raster file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- return self.run_tool('convert_nodata_to_zero', args, callback) # returns 1 if error
-
- def convert_raster_format(self, i, output, callback=None):
- """Converts raster data from one format to another.
-
- Keyword arguments:
-
- i -- Input raster file.
- output -- Output raster file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- return self.run_tool('convert_raster_format', args, callback) # returns 1 if error
-
- def export_table_to_csv(self, i, output, headers=True, callback=None):
- """Exports an attribute table to a CSV text file.
-
- Keyword arguments:
-
- i -- Input vector file.
- output -- Output raster file.
- headers -- Export field names as file header?.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- if headers: args.append("--headers")
- return self.run_tool('export_table_to_csv', args, callback) # returns 1 if error
-
- def join_tables(self, input1, pkey, input2, fkey, import_field, callback=None):
- """Merge a vector's attribute table with another table based on a common field.
-
- Keyword arguments:
-
- input1 -- Input primary vector file (i.e. the table to be modified).
- pkey -- Primary key field.
- input2 -- Input foreign vector file (i.e. source of data to be imported).
- fkey -- Foreign key field.
- import_field -- Imported field (all fields will be imported if not specified).
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input1='{}'".format(input1))
- args.append("--pkey='{}'".format(pkey))
- args.append("--input2='{}'".format(input2))
- args.append("--fkey='{}'".format(fkey))
- args.append("--import_field='{}'".format(import_field))
- return self.run_tool('join_tables', args, callback) # returns 1 if error
-
- def lines_to_polygons(self, i, output, callback=None):
- """Converts vector polylines to polygons.
-
- Keyword arguments:
-
- i -- Input vector line file.
- output -- Output vector polygon file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- return self.run_tool('lines_to_polygons', args, callback) # returns 1 if error
-
- def merge_table_with_csv(self, i, pkey, csv, fkey, import_field=None, callback=None):
- """Merge a vector's attribute table with a table contained within a CSV text file.
-
- Keyword arguments:
-
- i -- Input primary vector file (i.e. the table to be modified).
- pkey -- Primary key field.
- csv -- Input CSV file (i.e. source of data to be imported).
- fkey -- Foreign key field.
- import_field -- Imported field (all fields will be imported if not specified).
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--pkey='{}'".format(pkey))
- args.append("--csv='{}'".format(csv))
- args.append("--fkey='{}'".format(fkey))
- if import_field is not None: args.append("--import_field='{}'".format(import_field))
- return self.run_tool('merge_table_with_csv', args, callback) # returns 1 if error
-
- def merge_vectors(self, inputs, output, callback=None):
- """Combines two or more input vectors of the same ShapeType creating a single, new output vector.
-
- Keyword arguments:
-
- inputs -- Input vector files.
- output -- Output vector file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--inputs='{}'".format(inputs))
- args.append("--output='{}'".format(output))
- return self.run_tool('merge_vectors', args, callback) # returns 1 if error
-
- def multi_part_to_single_part(self, i, output, exclude_holes=True, callback=None):
- """Converts a vector file containing multi-part features into a vector containing only single-part features.
-
- Keyword arguments:
-
- i -- Input vector line or polygon file.
- output -- Output vector line or polygon file.
- exclude_holes -- Exclude hole parts from the feature splitting? (holes will continue to belong to their features in output.).
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- if exclude_holes: args.append("--exclude_holes")
- return self.run_tool('multi_part_to_single_part', args, callback) # returns 1 if error
-
- def new_raster_from_base(self, base, output, value="nodata", data_type="float", callback=None):
- """Creates a new raster using a base image.
-
- Keyword arguments:
-
- base -- Input base raster file.
- output -- Output raster file.
- value -- Constant value to fill raster with; either 'nodata' or numeric value.
- data_type -- Output raster data type; options include 'double' (64-bit), 'float' (32-bit), and 'integer' (signed 16-bit) (default is 'float').
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--base='{}'".format(base))
- args.append("--output='{}'".format(output))
- args.append("--value={}".format(value))
- args.append("--data_type={}".format(data_type))
- return self.run_tool('new_raster_from_base', args, callback) # returns 1 if error
-
- def polygons_to_lines(self, i, output, callback=None):
- """Converts vector polygons to polylines.
-
- Keyword arguments:
-
- i -- Input vector polygon file.
- output -- Output vector lines file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- return self.run_tool('polygons_to_lines', args, callback) # returns 1 if error
-
- def print_geo_tiff_tags(self, i, callback=None):
- """Prints the tags within a GeoTIFF.
-
- Keyword arguments:
-
- i -- Input GeoTIFF file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- return self.run_tool('print_geo_tiff_tags', args, callback) # returns 1 if error
-
- def raster_to_vector_lines(self, i, output, callback=None):
- """Converts a raster lines features into a vector of the POLYLINE shapetype.
-
- Keyword arguments:
-
- i -- Input raster lines file.
- output -- Output raster file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- return self.run_tool('raster_to_vector_lines', args, callback) # returns 1 if error
-
- def raster_to_vector_points(self, i, output, callback=None):
- """Converts a raster dataset to a vector of the POINT shapetype.
-
- Keyword arguments:
-
- i -- Input raster file.
- output -- Output vector points file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- return self.run_tool('raster_to_vector_points', args, callback) # returns 1 if error
-
- def reinitialize_attribute_table(self, i, callback=None):
- """Reinitializes a vector's attribute table deleting all fields but the feature ID (FID).
-
- Keyword arguments:
-
- i -- Input vector file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- return self.run_tool('reinitialize_attribute_table', args, callback) # returns 1 if error
-
- def remove_polygon_holes(self, i, output, callback=None):
- """Removes holes within the features of a vector polygon file.
-
- Keyword arguments:
-
- i -- Input vector polygon file.
- output -- Output vector polygon file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- return self.run_tool('remove_polygon_holes', args, callback) # returns 1 if error
-
- def set_nodata_value(self, i, output, back_value=0.0, callback=None):
- """Assign a specified value in an input image to the NoData value.
-
- Keyword arguments:
-
- i -- Input raster file.
- output -- Output raster file.
- back_value -- Background value to set to nodata.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- args.append("--back_value={}".format(back_value))
- return self.run_tool('set_nodata_value', args, callback) # returns 1 if error
-
- def single_part_to_multi_part(self, i, output, field=None, callback=None):
- """Converts a vector file containing multi-part features into a vector containing only single-part features.
-
- Keyword arguments:
-
- i -- Input vector line or polygon file.
- field -- Grouping ID field name in attribute table.
- output -- Output vector line or polygon file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- if field is not None: args.append("--field='{}'".format(field))
- args.append("--output='{}'".format(output))
- return self.run_tool('single_part_to_multi_part', args, callback) # returns 1 if error
-
- def vector_lines_to_raster(self, i, output, field="FID", nodata=True, cell_size=None, base=None, callback=None):
- """Converts a vector containing polylines into a raster.
-
- Keyword arguments:
-
- i -- Input vector lines file.
- field -- Input field name in attribute table.
- output -- Output raster file.
- nodata -- Background value to set to NoData. Without this flag, it will be set to 0.0.
- cell_size -- Optionally specified cell size of output raster. Not used when base raster is specified.
- base -- Optionally specified input base raster file. Not used when a cell size is specified.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--field={}".format(field))
- args.append("--output='{}'".format(output))
- if nodata: args.append("--nodata")
- if cell_size is not None: args.append("--cell_size='{}'".format(cell_size))
- if base is not None: args.append("--base='{}'".format(base))
- return self.run_tool('vector_lines_to_raster', args, callback) # returns 1 if error
-
- def vector_points_to_raster(self, i, output, field="FID", assign="last", nodata=True, cell_size=None, base=None, callback=None):
- """Converts a vector containing points into a raster.
-
- Keyword arguments:
-
- i -- Input vector Points file.
- field -- Input field name in attribute table.
- output -- Output raster file.
- assign -- Assignment operation, where multiple points are in the same grid cell; options include 'first', 'last' (default), 'min', 'max', 'sum'.
- nodata -- Background value to set to NoData. Without this flag, it will be set to 0.0.
- cell_size -- Optionally specified cell size of output raster. Not used when base raster is specified.
- base -- Optionally specified input base raster file. Not used when a cell size is specified.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--field={}".format(field))
- args.append("--output='{}'".format(output))
- args.append("--assign={}".format(assign))
- if nodata: args.append("--nodata")
- if cell_size is not None: args.append("--cell_size='{}'".format(cell_size))
- if base is not None: args.append("--base='{}'".format(base))
- return self.run_tool('vector_points_to_raster', args, callback) # returns 1 if error
-
- def vector_polygons_to_raster(self, i, output, field="FID", nodata=True, cell_size=None, base=None, callback=None):
- """Converts a vector containing polygons into a raster.
-
- Keyword arguments:
-
- i -- Input vector polygons file.
- field -- Input field name in attribute table.
- output -- Output raster file.
- nodata -- Background value to set to NoData. Without this flag, it will be set to 0.0.
- cell_size -- Optionally specified cell size of output raster. Not used when base raster is specified.
- base -- Optionally specified input base raster file. Not used when a cell size is specified.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--field={}".format(field))
- args.append("--output='{}'".format(output))
- if nodata: args.append("--nodata")
- if cell_size is not None: args.append("--cell_size='{}'".format(cell_size))
- if base is not None: args.append("--base='{}'".format(base))
- return self.run_tool('vector_polygons_to_raster', args, callback) # returns 1 if error
-
- ################
- # GIS Analysis #
- ################
-
- def aggregate_raster(self, i, output, agg_factor=2, type="mean", callback=None):
- """Aggregates a raster to a lower resolution.
-
- Keyword arguments:
-
- i -- Input raster file.
- output -- Output raster file.
- agg_factor -- Aggregation factor, in pixels.
- type -- Statistic used to fill output pixels.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- args.append("--agg_factor={}".format(agg_factor))
- args.append("--type={}".format(type))
- return self.run_tool('aggregate_raster', args, callback) # returns 1 if error
-
- def block_maximum_gridding(self, i, field, output, use_z=False, cell_size=None, base=None, callback=None):
- """Creates a raster grid based on a set of vector points and assigns grid values using a block maximum scheme.
-
- Keyword arguments:
-
- i -- Input vector Points file.
- field -- Input field name in attribute table.
- use_z -- Use z-coordinate instead of field?.
- output -- Output raster file.
- cell_size -- Optionally specified cell size of output raster. Not used when base raster is specified.
- base -- Optionally specified input base raster file. Not used when a cell size is specified.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--field='{}'".format(field))
- if use_z: args.append("--use_z")
- args.append("--output='{}'".format(output))
- if cell_size is not None: args.append("--cell_size='{}'".format(cell_size))
- if base is not None: args.append("--base='{}'".format(base))
- return self.run_tool('block_maximum_gridding', args, callback) # returns 1 if error
-
- def block_minimum_gridding(self, i, field, output, use_z=False, cell_size=None, base=None, callback=None):
- """Creates a raster grid based on a set of vector points and assigns grid values using a block minimum scheme.
-
- Keyword arguments:
-
- i -- Input vector Points file.
- field -- Input field name in attribute table.
- use_z -- Use z-coordinate instead of field?.
- output -- Output raster file.
- cell_size -- Optionally specified cell size of output raster. Not used when base raster is specified.
- base -- Optionally specified input base raster file. Not used when a cell size is specified.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--field='{}'".format(field))
- if use_z: args.append("--use_z")
- args.append("--output='{}'".format(output))
- if cell_size is not None: args.append("--cell_size='{}'".format(cell_size))
- if base is not None: args.append("--base='{}'".format(base))
- return self.run_tool('block_minimum_gridding', args, callback) # returns 1 if error
-
- def centroid(self, i, output, text_output=False, callback=None):
- """Calculates the centroid, or average location, of raster polygon objects.
-
- Keyword arguments:
-
- i -- Input raster file.
- output -- Output raster file.
- text_output -- Optional text output.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- if text_output: args.append("--text_output")
- return self.run_tool('centroid', args, callback) # returns 1 if error
-
- def centroid_vector(self, i, output, callback=None):
- """Identifes the centroid point of a vector polyline or polygon feature or a group of vector points.
-
- Keyword arguments:
-
- i -- Input vector file.
- output -- Output vector file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- return self.run_tool('centroid_vector', args, callback) # returns 1 if error
-
- def clump(self, i, output, diag=True, zero_back=False, callback=None):
- """Groups cells that form discrete areas, assigning them unique identifiers.
-
- Keyword arguments:
-
- i -- Input raster file.
- output -- Output raster file.
- diag -- Flag indicating whether diagonal connections should be considered.
- zero_back -- Flag indicating whether zero values should be treated as a background.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- if diag: args.append("--diag")
- if zero_back: args.append("--zero_back")
- return self.run_tool('clump', args, callback) # returns 1 if error
-
- def construct_vector_tin(self, i, output, field=None, use_z=False, callback=None):
- """Creates a vector triangular irregular network (TIN) for a set of vector points.
-
- Keyword arguments:
-
- i -- Input vector points file.
- field -- Input field name in attribute table.
- use_z -- Use the 'z' dimension of the Shapefile's geometry instead of an attribute field?.
- output -- Output vector polygon file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- if field is not None: args.append("--field='{}'".format(field))
- if use_z: args.append("--use_z")
- args.append("--output='{}'".format(output))
- return self.run_tool('construct_vector_tin', args, callback) # returns 1 if error
-
- def create_hexagonal_vector_grid(self, i, output, width, orientation="horizontal", callback=None):
- """Creates a hexagonal vector grid.
-
- Keyword arguments:
-
- i -- Input base file.
- output -- Output vector polygon file.
- width -- The grid cell width.
- orientation -- Grid Orientation, 'horizontal' or 'vertical'.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- args.append("--width='{}'".format(width))
- args.append("--orientation={}".format(orientation))
- return self.run_tool('create_hexagonal_vector_grid', args, callback) # returns 1 if error
-
- def create_plane(self, base, output, gradient=15.0, aspect=90.0, constant=0.0, callback=None):
- """Creates a raster image based on the equation for a simple plane.
-
- Keyword arguments:
-
- base -- Input base raster file.
- output -- Output raster file.
- gradient -- Slope gradient in degrees (-85.0 to 85.0).
- aspect -- Aspect (direction) in degrees clockwise from north (0.0-360.0).
- constant -- Constant value.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--base='{}'".format(base))
- args.append("--output='{}'".format(output))
- args.append("--gradient={}".format(gradient))
- args.append("--aspect={}".format(aspect))
- args.append("--constant={}".format(constant))
- return self.run_tool('create_plane', args, callback) # returns 1 if error
-
- def create_rectangular_vector_grid(self, i, output, width, height, xorig=0, yorig=0, callback=None):
- """Creates a rectangular vector grid.
-
- Keyword arguments:
-
- i -- Input base file.
- output -- Output vector polygon file.
- width -- The grid cell width.
- height -- The grid cell height.
- xorig -- The grid origin x-coordinate.
- yorig -- The grid origin y-coordinate.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- args.append("--width='{}'".format(width))
- args.append("--height='{}'".format(height))
- args.append("--xorig={}".format(xorig))
- args.append("--yorig={}".format(yorig))
- return self.run_tool('create_rectangular_vector_grid', args, callback) # returns 1 if error
-
- def dissolve(self, i, output, field=None, snap=0.0, callback=None):
- """Removes the interior, or shared, boundaries within a vector polygon coverage.
-
- Keyword arguments:
-
- i -- Input vector file.
- field -- Dissolve field attribute (optional).
- output -- Output vector file.
- snap -- Snap tolerance.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- if field is not None: args.append("--field='{}'".format(field))
- args.append("--output='{}'".format(output))
- args.append("--snap={}".format(snap))
- return self.run_tool('dissolve', args, callback) # returns 1 if error
-
- def eliminate_coincident_points(self, i, output, tolerance, callback=None):
- """Removes any coincident, or nearly coincident, points from a vector points file.
-
- Keyword arguments:
-
- i -- Input vector file.
- output -- Output vector polygon file.
- tolerance -- The distance tolerance for points.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- args.append("--tolerance='{}'".format(tolerance))
- return self.run_tool('eliminate_coincident_points', args, callback) # returns 1 if error
-
- def extend_vector_lines(self, i, output, dist, extend="both ends", callback=None):
- """Extends vector lines by a specified distance.
-
- Keyword arguments:
-
- i -- Input vector polyline file.
- output -- Output vector polyline file.
- dist -- The distance to extend.
- extend -- Extend direction, 'both ends' (default), 'line start', 'line end'.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- args.append("--dist='{}'".format(dist))
- args.append("--extend={}".format(extend))
- return self.run_tool('extend_vector_lines', args, callback) # returns 1 if error
-
- def extract_nodes(self, i, output, callback=None):
- """Converts vector lines or polygons into vertex points.
-
- Keyword arguments:
-
- i -- Input vector lines or polygon file.
- output -- Output vector points file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- return self.run_tool('extract_nodes', args, callback) # returns 1 if error
-
- def extract_raster_values_at_points(self, inputs, points, out_text=False, callback=None):
- """Extracts the values of raster(s) at vector point locations.
-
- Keyword arguments:
-
- inputs -- Input raster files.
- points -- Input vector points file.
- out_text -- Output point values as text? Otherwise, the only output is to to the points file's attribute table.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--inputs='{}'".format(inputs))
- args.append("--points='{}'".format(points))
- if out_text: args.append("--out_text")
- return self.run_tool('extract_raster_values_at_points', args, callback) # returns 1 if error
-
- def find_lowest_or_highest_points(self, i, output, out_type="lowest", callback=None):
- """Locates the lowest and/or highest valued cells in a raster.
-
- Keyword arguments:
-
- i -- Input raster file.
- output -- Output vector points file.
- out_type -- Output type; one of 'area' (default) and 'volume'.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- args.append("--out_type={}".format(out_type))
- return self.run_tool('find_lowest_or_highest_points', args, callback) # returns 1 if error
-
- def idw_interpolation(self, i, field, output, use_z=False, weight=2.0, radius=None, min_points=None, cell_size=None, base=None, callback=None):
- """Interpolates vector points into a raster surface using an inverse-distance weighted scheme.
-
- Keyword arguments:
-
- i -- Input vector Points file.
- field -- Input field name in attribute table.
- use_z -- Use z-coordinate instead of field?.
- output -- Output raster file.
- weight -- IDW weight value.
- radius -- Search Radius.
- min_points -- Minimum number of points.
- cell_size -- Optionally specified cell size of output raster. Not used when base raster is specified.
- base -- Optionally specified input base raster file. Not used when a cell size is specified.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--field='{}'".format(field))
- if use_z: args.append("--use_z")
- args.append("--output='{}'".format(output))
- args.append("--weight={}".format(weight))
- if radius is not None: args.append("--radius='{}'".format(radius))
- if min_points is not None: args.append("--min_points='{}'".format(min_points))
- if cell_size is not None: args.append("--cell_size='{}'".format(cell_size))
- if base is not None: args.append("--base='{}'".format(base))
- return self.run_tool('idw_interpolation', args, callback) # returns 1 if error
-
- def layer_footprint(self, i, output, callback=None):
- """Creates a vector polygon footprint of the area covered by a raster grid or vector layer.
-
- Keyword arguments:
-
- i -- Input raster or vector file.
- output -- Output vector polygon file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- return self.run_tool('layer_footprint', args, callback) # returns 1 if error
-
- def medoid(self, i, output, callback=None):
- """Calculates the medoid for a series of vector features contained in a shapefile.
-
- Keyword arguments:
-
- i -- Input vector file.
- output -- Output vector file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- return self.run_tool('medoid', args, callback) # returns 1 if error
-
- def minimum_bounding_box(self, i, output, criterion="area", features=True, callback=None):
- """Creates a vector minimum bounding rectangle around vector features.
-
- Keyword arguments:
-
- i -- Input vector file.
- output -- Output vector polygon file.
- criterion -- Minimization criterion; options include 'area' (default), 'length', 'width', and 'perimeter'.
- features -- Find the minimum bounding rectangles around each individual vector feature.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- args.append("--criterion={}".format(criterion))
- if features: args.append("--features")
- return self.run_tool('minimum_bounding_box', args, callback) # returns 1 if error
-
- def minimum_bounding_circle(self, i, output, features=True, callback=None):
- """Delineates the minimum bounding circle (i.e. smallest enclosing circle) for a group of vectors.
-
- Keyword arguments:
-
- i -- Input vector file.
- output -- Output vector polygon file.
- features -- Find the minimum bounding circle around each individual vector feature.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- if features: args.append("--features")
- return self.run_tool('minimum_bounding_circle', args, callback) # returns 1 if error
-
- def minimum_bounding_envelope(self, i, output, features=True, callback=None):
- """Creates a vector axis-aligned minimum bounding rectangle (envelope) around vector features.
-
- Keyword arguments:
-
- i -- Input vector file.
- output -- Output vector polygon file.
- features -- Find the minimum bounding envelop around each individual vector feature.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- if features: args.append("--features")
- return self.run_tool('minimum_bounding_envelope', args, callback) # returns 1 if error
-
- def minimum_convex_hull(self, i, output, features=True, callback=None):
- """Creates a vector convex polygon around vector features.
-
- Keyword arguments:
-
- i -- Input vector file.
- output -- Output vector polygon file.
- features -- Find the hulls around each vector feature.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- if features: args.append("--features")
- return self.run_tool('minimum_convex_hull', args, callback) # returns 1 if error
-
- def nearest_neighbour_gridding(self, i, field, output, use_z=False, cell_size=None, base=None, max_dist=None, callback=None):
- """Creates a raster grid based on a set of vector points and assigns grid values using the nearest neighbour.
-
- Keyword arguments:
-
- i -- Input vector Points file.
- field -- Input field name in attribute table.
- use_z -- Use z-coordinate instead of field?.
- output -- Output raster file.
- cell_size -- Optionally specified cell size of output raster. Not used when base raster is specified.
- base -- Optionally specified input base raster file. Not used when a cell size is specified.
- max_dist -- Maximum search distance (optional).
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--field='{}'".format(field))
- if use_z: args.append("--use_z")
- args.append("--output='{}'".format(output))
- if cell_size is not None: args.append("--cell_size='{}'".format(cell_size))
- if base is not None: args.append("--base='{}'".format(base))
- if max_dist is not None: args.append("--max_dist='{}'".format(max_dist))
- return self.run_tool('nearest_neighbour_gridding', args, callback) # returns 1 if error
-
- def polygon_area(self, i, callback=None):
- """Calculates the area of vector polygons.
-
- Keyword arguments:
-
- i -- Input vector polygon file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- return self.run_tool('polygon_area', args, callback) # returns 1 if error
-
- def polygon_long_axis(self, i, output, callback=None):
- """This tool can be used to map the long axis of polygon features.
-
- Keyword arguments:
-
- i -- Input vector polygons file.
- output -- Output vector polygon file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- return self.run_tool('polygon_long_axis', args, callback) # returns 1 if error
-
- def polygon_perimeter(self, i, callback=None):
- """Calculates the perimeter of vector polygons.
-
- Keyword arguments:
-
- i -- Input vector polygon file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- return self.run_tool('polygon_perimeter', args, callback) # returns 1 if error
-
- def polygon_short_axis(self, i, output, callback=None):
- """This tool can be used to map the short axis of polygon features.
-
- Keyword arguments:
-
- i -- Input vector polygons file.
- output -- Output vector polygon file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- return self.run_tool('polygon_short_axis', args, callback) # returns 1 if error
-
- def raster_area(self, i, output=None, out_text=False, units="grid cells", zero_back=False, callback=None):
- """Calculates the area of polygons or classes within a raster image.
-
- Keyword arguments:
-
- i -- Input raster file.
- output -- Output raster file.
- out_text -- Would you like to output polygon areas to text?.
- units -- Area units; options include 'grid cells' and 'map units'.
- zero_back -- Flag indicating whether zero values should be treated as a background.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- if output is not None: args.append("--output='{}'".format(output))
- if out_text: args.append("--out_text")
- args.append("--units={}".format(units))
- if zero_back: args.append("--zero_back")
- return self.run_tool('raster_area', args, callback) # returns 1 if error
-
- def raster_cell_assignment(self, i, output, assign="column", callback=None):
- """Assign row or column number to cells.
-
- Keyword arguments:
-
- i -- Input raster file.
- output -- Output raster file.
- assign -- Which variable would you like to assign to grid cells? Options include 'column', 'row', 'x', and 'y'.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- args.append("--assign={}".format(assign))
- return self.run_tool('raster_cell_assignment', args, callback) # returns 1 if error
-
- def reclass(self, i, output, reclass_vals, assign_mode=False, callback=None):
- """Reclassifies the values in a raster image.
-
- Keyword arguments:
-
- i -- Input raster file.
- output -- Output raster file.
- reclass_vals -- Reclassification triplet values (new value; from value; to less than), e.g. '0.0;0.0;1.0;1.0;1.0;2.0'.
- assign_mode -- Optional Boolean flag indicating whether to operate in assign mode, reclass_vals values are interpreted as new value; old value pairs.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- args.append("--reclass_vals='{}'".format(reclass_vals))
- if assign_mode: args.append("--assign_mode")
- return self.run_tool('reclass', args, callback) # returns 1 if error
-
- def reclass_equal_interval(self, i, output, interval=10.0, start_val=None, end_val=None, callback=None):
- """Reclassifies the values in a raster image based on equal-ranges.
-
- Keyword arguments:
-
- i -- Input raster file.
- output -- Output raster file.
- interval -- Class interval size.
- start_val -- Optional starting value (default is input minimum value).
- end_val -- Optional ending value (default is input maximum value).
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- args.append("--interval={}".format(interval))
- if start_val is not None: args.append("--start_val='{}'".format(start_val))
- if end_val is not None: args.append("--end_val='{}'".format(end_val))
- return self.run_tool('reclass_equal_interval', args, callback) # returns 1 if error
-
- def reclass_from_file(self, i, reclass_file, output, callback=None):
- """Reclassifies the values in a raster image using reclass ranges in a text file.
-
- Keyword arguments:
-
- i -- Input raster file.
- reclass_file -- Input text file containing reclass ranges.
- output -- Output raster file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--reclass_file='{}'".format(reclass_file))
- args.append("--output='{}'".format(output))
- return self.run_tool('reclass_from_file', args, callback) # returns 1 if error
-
- def smooth_vectors(self, i, output, filter=3, callback=None):
- """Smooths a vector coverage of either a POLYLINE or POLYGON base ShapeType.
-
- Keyword arguments:
-
- i -- Input vector POLYLINE or POLYGON file.
- output -- Output vector file.
- filter -- The filter size, any odd integer greater than or equal to 3; default is 3.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- args.append("--filter={}".format(filter))
- return self.run_tool('smooth_vectors', args, callback) # returns 1 if error
-
- def tin_gridding(self, i, output, resolution, field=None, use_z=False, callback=None):
- """Creates a raster grid based on a triangular irregular network (TIN) fitted to vector points.
-
- Keyword arguments:
-
- i -- Input vector points file.
- field -- Input field name in attribute table.
- use_z -- Use the 'z' dimension of the Shapefile's geometry instead of an attribute field?.
- output -- Output raster file.
- resolution -- Output raster's grid resolution.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- if field is not None: args.append("--field='{}'".format(field))
- if use_z: args.append("--use_z")
- args.append("--output='{}'".format(output))
- args.append("--resolution='{}'".format(resolution))
- return self.run_tool('tin_gridding', args, callback) # returns 1 if error
-
- def vector_hex_binning(self, i, output, width, orientation="horizontal", callback=None):
- """Hex-bins a set of vector points.
-
- Keyword arguments:
-
- i -- Input base file.
- output -- Output vector polygon file.
- width -- The grid cell width.
- orientation -- Grid Orientation, 'horizontal' or 'vertical'.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- args.append("--width='{}'".format(width))
- args.append("--orientation={}".format(orientation))
- return self.run_tool('vector_hex_binning', args, callback) # returns 1 if error
-
- def voronoi_diagram(self, i, output, callback=None):
- """Creates a vector Voronoi diagram for a set of vector points.
-
- Keyword arguments:
-
- i -- Input vector points file.
- output -- Output vector polygon file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- return self.run_tool('voronoi_diagram', args, callback) # returns 1 if error
-
- ###############################
- # GIS Analysis/Distance Tools #
- ###############################
-
- def buffer_raster(self, i, output, size, gridcells=False, callback=None):
- """Maps a distance-based buffer around each non-background (non-zero/non-nodata) grid cell in an input image.
-
- Keyword arguments:
-
- i -- Input raster file.
- output -- Output raster file.
- size -- Buffer size.
- gridcells -- Optional flag to indicate that the 'size' threshold should be measured in grid cells instead of the default map units.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- args.append("--size='{}'".format(size))
- if gridcells: args.append("--gridcells")
- return self.run_tool('buffer_raster', args, callback) # returns 1 if error
-
- def cost_allocation(self, source, backlink, output, callback=None):
- """Identifies the source cell to which each grid cell is connected by a least-cost pathway in a cost-distance analysis.
-
- Keyword arguments:
-
- source -- Input source raster file.
- backlink -- Input backlink raster file generated by the cost-distance tool.
- output -- Output raster file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--source='{}'".format(source))
- args.append("--backlink='{}'".format(backlink))
- args.append("--output='{}'".format(output))
- return self.run_tool('cost_allocation', args, callback) # returns 1 if error
-
- def cost_distance(self, source, cost, out_accum, out_backlink, callback=None):
- """Performs cost-distance accumulation on a cost surface and a group of source cells.
-
- Keyword arguments:
-
- source -- Input source raster file.
- cost -- Input cost (friction) raster file.
- out_accum -- Output cost accumulation raster file.
- out_backlink -- Output backlink raster file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--source='{}'".format(source))
- args.append("--cost='{}'".format(cost))
- args.append("--out_accum='{}'".format(out_accum))
- args.append("--out_backlink='{}'".format(out_backlink))
- return self.run_tool('cost_distance', args, callback) # returns 1 if error
-
- def cost_pathway(self, destination, backlink, output, zero_background=False, callback=None):
- """Performs cost-distance pathway analysis using a series of destination grid cells.
-
- Keyword arguments:
-
- destination -- Input destination raster file.
- backlink -- Input backlink raster file generated by the cost-distance tool.
- output -- Output cost pathway raster file.
- zero_background -- Flag indicating whether zero values should be treated as a background.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--destination='{}'".format(destination))
- args.append("--backlink='{}'".format(backlink))
- args.append("--output='{}'".format(output))
- if zero_background: args.append("--zero_background")
- return self.run_tool('cost_pathway', args, callback) # returns 1 if error
-
- def euclidean_allocation(self, i, output, callback=None):
- """Assigns grid cells in the output raster the value of the nearest target cell in the input image, measured by the Shih and Wu (2004) Euclidean distance transform.
-
- Keyword arguments:
-
- i -- Input raster file.
- output -- Output raster file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- return self.run_tool('euclidean_allocation', args, callback) # returns 1 if error
-
- def euclidean_distance(self, i, output, callback=None):
- """Calculates the Shih and Wu (2004) Euclidean distance transform.
-
- Keyword arguments:
-
- i -- Input raster file.
- output -- Output raster file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- return self.run_tool('euclidean_distance', args, callback) # returns 1 if error
-
- ##############################
- # GIS Analysis/Overlay Tools #
- ##############################
-
- def average_overlay(self, inputs, output, callback=None):
- """Calculates the average for each grid cell from a group of raster images.
-
- Keyword arguments:
-
- inputs -- Input raster files.
- output -- Output raster file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--inputs='{}'".format(inputs))
- args.append("--output='{}'".format(output))
- return self.run_tool('average_overlay', args, callback) # returns 1 if error
-
- def clip(self, i, clip, output, callback=None):
- """Extract all the features, or parts of features, that overlap with the features of the clip vector.
-
- Keyword arguments:
-
- i -- Input vector file.
- clip -- Input clip polygon vector file.
- output -- Output vector file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--clip='{}'".format(clip))
- args.append("--output='{}'".format(output))
- return self.run_tool('clip', args, callback) # returns 1 if error
-
- def clip_raster_to_polygon(self, i, polygons, output, maintain_dimensions=False, callback=None):
- """Clips a raster to a vector polygon.
-
- Keyword arguments:
-
- i -- Input raster file.
- polygons -- Input vector polygons file.
- output -- Output raster file.
- maintain_dimensions -- Maintain input raster dimensions?.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--polygons='{}'".format(polygons))
- args.append("--output='{}'".format(output))
- if maintain_dimensions: args.append("--maintain_dimensions")
- return self.run_tool('clip_raster_to_polygon', args, callback) # returns 1 if error
-
- def count_if(self, inputs, output, value, callback=None):
- """Counts the number of occurrences of a specified value in a cell-stack of rasters.
-
- Keyword arguments:
-
- inputs -- Input raster files.
- output -- Output raster file.
- value -- Search value (e.g. countif value = 5.0).
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--inputs='{}'".format(inputs))
- args.append("--output='{}'".format(output))
- args.append("--value='{}'".format(value))
- return self.run_tool('count_if', args, callback) # returns 1 if error
-
- def difference(self, i, overlay, output, callback=None):
- """Outputs the features that occur in one of the two vector inputs but not both, i.e. no overlapping features.
-
- Keyword arguments:
-
- i -- Input vector file.
- overlay -- Input overlay vector file.
- output -- Output vector file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--overlay='{}'".format(overlay))
- args.append("--output='{}'".format(output))
- return self.run_tool('difference', args, callback) # returns 1 if error
-
- def erase(self, i, erase, output, callback=None):
- """Removes all the features, or parts of features, that overlap with the features of the erase vector polygon.
-
- Keyword arguments:
-
- i -- Input vector file.
- erase -- Input erase polygon vector file.
- output -- Output vector file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--erase='{}'".format(erase))
- args.append("--output='{}'".format(output))
- return self.run_tool('erase', args, callback) # returns 1 if error
-
- def erase_polygon_from_raster(self, i, polygons, output, callback=None):
- """Erases (cuts out) a vector polygon from a raster.
-
- Keyword arguments:
-
- i -- Input raster file.
- polygons -- Input vector polygons file.
- output -- Output raster file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--polygons='{}'".format(polygons))
- args.append("--output='{}'".format(output))
- return self.run_tool('erase_polygon_from_raster', args, callback) # returns 1 if error
-
- def highest_position(self, inputs, output, callback=None):
- """Identifies the stack position of the maximum value within a raster stack on a cell-by-cell basis.
-
- Keyword arguments:
-
- inputs -- Input raster files.
- output -- Output raster file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--inputs='{}'".format(inputs))
- args.append("--output='{}'".format(output))
- return self.run_tool('highest_position', args, callback) # returns 1 if error
-
- def intersect(self, i, overlay, output, snap=0.0, callback=None):
- """Identifies the parts of features in common between two input vector layers.
-
- Keyword arguments:
-
- i -- Input vector file.
- overlay -- Input overlay vector file.
- output -- Output vector file.
- snap -- Snap tolerance.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--overlay='{}'".format(overlay))
- args.append("--output='{}'".format(output))
- args.append("--snap={}".format(snap))
- return self.run_tool('intersect', args, callback) # returns 1 if error
-
- def line_intersections(self, input1, input2, output, callback=None):
- """Identifies points where the features of two vector line layers intersect.
-
- Keyword arguments:
-
- input1 -- Input vector polyline file.
- input2 -- Input vector polyline file.
- output -- Output vector point file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input1='{}'".format(input1))
- args.append("--input2='{}'".format(input2))
- args.append("--output='{}'".format(output))
- return self.run_tool('line_intersections', args, callback) # returns 1 if error
-
- def lowest_position(self, inputs, output, callback=None):
- """Identifies the stack position of the minimum value within a raster stack on a cell-by-cell basis.
-
- Keyword arguments:
-
- inputs -- Input raster files.
- output -- Output raster file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--inputs='{}'".format(inputs))
- args.append("--output='{}'".format(output))
- return self.run_tool('lowest_position', args, callback) # returns 1 if error
-
- def max_absolute_overlay(self, inputs, output, callback=None):
- """Evaluates the maximum absolute value for each grid cell from a stack of input rasters.
-
- Keyword arguments:
-
- inputs -- Input raster files.
- output -- Output raster file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--inputs='{}'".format(inputs))
- args.append("--output='{}'".format(output))
- return self.run_tool('max_absolute_overlay', args, callback) # returns 1 if error
-
- def max_overlay(self, inputs, output, callback=None):
- """Evaluates the maximum value for each grid cell from a stack of input rasters.
-
- Keyword arguments:
-
- inputs -- Input raster files.
- output -- Output raster file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--inputs='{}'".format(inputs))
- args.append("--output='{}'".format(output))
- return self.run_tool('max_overlay', args, callback) # returns 1 if error
-
- def min_absolute_overlay(self, inputs, output, callback=None):
- """Evaluates the minimum absolute value for each grid cell from a stack of input rasters.
-
- Keyword arguments:
-
- inputs -- Input raster files.
- output -- Output raster file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--inputs='{}'".format(inputs))
- args.append("--output='{}'".format(output))
- return self.run_tool('min_absolute_overlay', args, callback) # returns 1 if error
-
- def min_overlay(self, inputs, output, callback=None):
- """Evaluates the minimum value for each grid cell from a stack of input rasters.
-
- Keyword arguments:
-
- inputs -- Input raster files.
- output -- Output raster file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--inputs='{}'".format(inputs))
- args.append("--output='{}'".format(output))
- return self.run_tool('min_overlay', args, callback) # returns 1 if error
-
- def percent_equal_to(self, inputs, comparison, output, callback=None):
- """Calculates the percentage of a raster stack that have cell values equal to an input on a cell-by-cell basis.
-
- Keyword arguments:
-
- inputs -- Input raster files.
- comparison -- Input comparison raster file.
- output -- Output raster file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--inputs='{}'".format(inputs))
- args.append("--comparison='{}'".format(comparison))
- args.append("--output='{}'".format(output))
- return self.run_tool('percent_equal_to', args, callback) # returns 1 if error
-
- def percent_greater_than(self, inputs, comparison, output, callback=None):
- """Calculates the percentage of a raster stack that have cell values greather than an input on a cell-by-cell basis.
-
- Keyword arguments:
-
- inputs -- Input raster files.
- comparison -- Input comparison raster file.
- output -- Output raster file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--inputs='{}'".format(inputs))
- args.append("--comparison='{}'".format(comparison))
- args.append("--output='{}'".format(output))
- return self.run_tool('percent_greater_than', args, callback) # returns 1 if error
-
- def percent_less_than(self, inputs, comparison, output, callback=None):
- """Calculates the percentage of a raster stack that have cell values less than an input on a cell-by-cell basis.
-
- Keyword arguments:
-
- inputs -- Input raster files.
- comparison -- Input comparison raster file.
- output -- Output raster file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--inputs='{}'".format(inputs))
- args.append("--comparison='{}'".format(comparison))
- args.append("--output='{}'".format(output))
- return self.run_tool('percent_less_than', args, callback) # returns 1 if error
-
- def pick_from_list(self, inputs, pos_input, output, callback=None):
- """Outputs the value from a raster stack specified by a position raster.
-
- Keyword arguments:
-
- inputs -- Input raster files.
- pos_input -- Input position raster file.
- output -- Output raster file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--inputs='{}'".format(inputs))
- args.append("--pos_input='{}'".format(pos_input))
- args.append("--output='{}'".format(output))
- return self.run_tool('pick_from_list', args, callback) # returns 1 if error
-
- def polygonize(self, inputs, output, callback=None):
- """Creates a polygon layer from two or more intersecting line features contained in one or more input vector line files.
-
- Keyword arguments:
-
- inputs -- Input vector polyline file.
- output -- Output vector polygon file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--inputs='{}'".format(inputs))
- args.append("--output='{}'".format(output))
- return self.run_tool('polygonize', args, callback) # returns 1 if error
-
- def split_with_lines(self, i, split, output, callback=None):
- """Splits the lines or polygons in one layer using the lines in another layer.
-
- Keyword arguments:
-
- i -- Input vector line or polygon file.
- split -- Input vector polyline file.
- output -- Output vector file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--split='{}'".format(split))
- args.append("--output='{}'".format(output))
- return self.run_tool('split_with_lines', args, callback) # returns 1 if error
-
- def sum_overlay(self, inputs, output, callback=None):
- """Calculates the sum for each grid cell from a group of raster images.
-
- Keyword arguments:
-
- inputs -- Input raster files.
- output -- Output raster file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--inputs='{}'".format(inputs))
- args.append("--output='{}'".format(output))
- return self.run_tool('sum_overlay', args, callback) # returns 1 if error
-
- def symmetrical_difference(self, i, overlay, output, snap=0.0, callback=None):
- """Outputs the features that occur in one of the two vector inputs but not both, i.e. no overlapping features.
-
- Keyword arguments:
-
- i -- Input vector file.
- overlay -- Input overlay vector file.
- output -- Output vector file.
- snap -- Snap tolerance.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--overlay='{}'".format(overlay))
- args.append("--output='{}'".format(output))
- args.append("--snap={}".format(snap))
- return self.run_tool('symmetrical_difference', args, callback) # returns 1 if error
-
- def union(self, i, overlay, output, snap=0.0, callback=None):
- """Splits vector layers at their overlaps, creating a layer containing all the portions from both input and overlay layers.
-
- Keyword arguments:
-
- i -- Input vector file.
- overlay -- Input overlay vector file.
- output -- Output vector file.
- snap -- Snap tolerance.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--overlay='{}'".format(overlay))
- args.append("--output='{}'".format(output))
- args.append("--snap={}".format(snap))
- return self.run_tool('union', args, callback) # returns 1 if error
-
- def weighted_overlay(self, factors, weights, output, cost=None, constraints=None, scale_max=1.0, callback=None):
- """Performs a weighted sum on multiple input rasters after converting each image to a common scale. The tool performs a multi-criteria evaluation (MCE).
-
- Keyword arguments:
-
- factors -- Input factor raster files.
- weights -- Weight values, contained in quotes and separated by commas or semicolons. Must have the same number as factors.
- cost -- Weight values, contained in quotes and separated by commas or semicolons. Must have the same number as factors.
- constraints -- Input constraints raster files.
- output -- Output raster file.
- scale_max -- Suitability scale maximum value (common values are 1.0, 100.0, and 255.0).
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--factors='{}'".format(factors))
- args.append("--weights='{}'".format(weights))
- if cost is not None: args.append("--cost='{}'".format(cost))
- if constraints is not None: args.append("--constraints='{}'".format(constraints))
- args.append("--output='{}'".format(output))
- args.append("--scale_max={}".format(scale_max))
- return self.run_tool('weighted_overlay', args, callback) # returns 1 if error
-
- def weighted_sum(self, inputs, weights, output, callback=None):
- """Performs a weighted-sum overlay on multiple input raster images.
-
- Keyword arguments:
-
- inputs -- Input raster files.
- weights -- Weight values, contained in quotes and separated by commas or semicolons.
- output -- Output raster file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--inputs='{}'".format(inputs))
- args.append("--weights='{}'".format(weights))
- args.append("--output='{}'".format(output))
- return self.run_tool('weighted_sum', args, callback) # returns 1 if error
-
- ##################################
- # GIS Analysis/Patch Shape Tools #
- ##################################
-
- def compactness_ratio(self, i, callback=None):
- """Calculates the compactness ratio (A/P), a measure of shape complexity, for vector polygons.
-
- Keyword arguments:
-
- i -- Input vector polygon file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- return self.run_tool('compactness_ratio', args, callback) # returns 1 if error
-
- def edge_proportion(self, i, output, output_text=False, callback=None):
- """Calculate the proportion of cells in a raster polygon that are edge cells.
-
- Keyword arguments:
-
- i -- Input raster file.
- output -- Output raster file.
- output_text -- flag indicating whether a text report should also be output.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- if output_text: args.append("--output_text")
- return self.run_tool('edge_proportion', args, callback) # returns 1 if error
-
- def elongation_ratio(self, i, callback=None):
- """Calculates the elongation ratio for vector polygons.
-
- Keyword arguments:
-
- i -- Input vector polygon file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- return self.run_tool('elongation_ratio', args, callback) # returns 1 if error
-
- def find_patch_or_class_edge_cells(self, i, output, callback=None):
- """Finds all cells located on the edge of patch or class features.
-
- Keyword arguments:
-
- i -- Input raster file.
- output -- Output raster file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- return self.run_tool('find_patch_or_class_edge_cells', args, callback) # returns 1 if error
-
- def hole_proportion(self, i, callback=None):
- """Calculates the proportion of the total area of a polygon's holes relative to the area of the polygon's hull.
-
- Keyword arguments:
-
- i -- Input vector polygon file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- return self.run_tool('hole_proportion', args, callback) # returns 1 if error
-
- def linearity_index(self, i, callback=None):
- """Calculates the linearity index for vector polygons.
-
- Keyword arguments:
-
- i -- Input vector polygon file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- return self.run_tool('linearity_index', args, callback) # returns 1 if error
-
- def narrowness_index(self, i, output, callback=None):
- """Calculates the area of polygons or classes within a raster image.
-
- Keyword arguments:
-
- i -- Input raster file.
- output -- Output raster file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- return self.run_tool('narrowness_index', args, callback) # returns 1 if error
-
- def patch_orientation(self, i, callback=None):
- """Calculates the orientation of vector polygons.
-
- Keyword arguments:
-
- i -- Input vector polygon file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- return self.run_tool('patch_orientation', args, callback) # returns 1 if error
-
- def perimeter_area_ratio(self, i, callback=None):
- """Calculates the perimeter-area ratio of vector polygons.
-
- Keyword arguments:
-
- i -- Input vector polygon file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- return self.run_tool('perimeter_area_ratio', args, callback) # returns 1 if error
-
- def radius_of_gyration(self, i, output, text_output=False, callback=None):
- """Calculates the distance of cells from their polygon's centroid.
-
- Keyword arguments:
-
- i -- Input raster file.
- output -- Output raster file.
- text_output -- Optional text output.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- if text_output: args.append("--text_output")
- return self.run_tool('radius_of_gyration', args, callback) # returns 1 if error
-
- def related_circumscribing_circle(self, i, callback=None):
- """Calculates the related circumscribing circle of vector polygons.
-
- Keyword arguments:
-
- i -- Input vector polygon file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- return self.run_tool('related_circumscribing_circle', args, callback) # returns 1 if error
-
- def shape_complexity_index(self, i, callback=None):
- """Calculates overall polygon shape complexity or irregularity.
-
- Keyword arguments:
-
- i -- Input vector polygon file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- return self.run_tool('shape_complexity_index', args, callback) # returns 1 if error
-
- def shape_complexity_index_raster(self, i, output, callback=None):
- """Calculates the area of polygons or classes within a raster image.
-
- Keyword arguments:
-
- i -- Input raster file.
- output -- Output raster file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- return self.run_tool('shape_complexity_index_raster', args, callback) # returns 1 if error
-
- ############################
- # Geomorphometric Analysis #
- ############################
-
- def aspect(self, dem, output, zfactor=1.0, callback=None):
- """Calculates an aspect raster from an input DEM.
-
- Keyword arguments:
-
- dem -- Input raster DEM file.
- output -- Output raster file.
- zfactor -- Optional multiplier for when the vertical and horizontal units are not the same.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--dem='{}'".format(dem))
- args.append("--output='{}'".format(output))
- args.append("--zfactor={}".format(zfactor))
- return self.run_tool('aspect', args, callback) # returns 1 if error
-
- def circular_variance_of_aspect(self, dem, output, filter=11, callback=None):
- """Calculates the circular variance of aspect at a scale for a DEM.
-
- Keyword arguments:
-
- dem -- Input raster DEM file.
- output -- Output raster roughness scale file.
- filter -- Size of the filter kernel.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--dem='{}'".format(dem))
- args.append("--output='{}'".format(output))
- args.append("--filter={}".format(filter))
- return self.run_tool('circular_variance_of_aspect', args, callback) # returns 1 if error
-
- def dev_from_mean_elev(self, dem, output, filterx=11, filtery=11, callback=None):
- """Calculates deviation from mean elevation.
-
- Keyword arguments:
-
- dem -- Input raster DEM file.
- output -- Output raster file.
- filterx -- Size of the filter kernel in the x-direction.
- filtery -- Size of the filter kernel in the y-direction.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--dem='{}'".format(dem))
- args.append("--output='{}'".format(output))
- args.append("--filterx={}".format(filterx))
- args.append("--filtery={}".format(filtery))
- return self.run_tool('dev_from_mean_elev', args, callback) # returns 1 if error
-
- def diff_from_mean_elev(self, dem, output, filterx=11, filtery=11, callback=None):
- """Calculates difference from mean elevation (equivalent to a high-pass filter).
-
- Keyword arguments:
-
- dem -- Input raster DEM file.
- output -- Output raster file.
- filterx -- Size of the filter kernel in the x-direction.
- filtery -- Size of the filter kernel in the y-direction.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--dem='{}'".format(dem))
- args.append("--output='{}'".format(output))
- args.append("--filterx={}".format(filterx))
- args.append("--filtery={}".format(filtery))
- return self.run_tool('diff_from_mean_elev', args, callback) # returns 1 if error
-
- def directional_relief(self, dem, output, azimuth=0.0, max_dist=None, callback=None):
- """Calculates relief for cells in an input DEM for a specified direction.
-
- Keyword arguments:
-
- dem -- Input raster DEM file.
- output -- Output raster file.
- azimuth -- Wind azimuth in degrees.
- max_dist -- Optional maximum search distance (unspecified if none; in xy units).
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--dem='{}'".format(dem))
- args.append("--output='{}'".format(output))
- args.append("--azimuth={}".format(azimuth))
- if max_dist is not None: args.append("--max_dist='{}'".format(max_dist))
- return self.run_tool('directional_relief', args, callback) # returns 1 if error
-
- def downslope_index(self, dem, output, drop=2.0, out_type="tangent", callback=None):
- """Calculates the Hjerdt et al. (2004) downslope index.
-
- Keyword arguments:
-
- dem -- Input raster DEM file.
- output -- Output raster file.
- drop -- Vertical drop value (default is 2.0).
- out_type -- Output type, options include 'tangent', 'degrees', 'radians', 'distance' (default is 'tangent').
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--dem='{}'".format(dem))
- args.append("--output='{}'".format(output))
- args.append("--drop={}".format(drop))
- args.append("--out_type={}".format(out_type))
- return self.run_tool('downslope_index', args, callback) # returns 1 if error
-
- def drainage_preserving_smoothing(self, dem, output, filter=11, norm_diff=15.0, num_iter=3, max_diff=0.5, reduction=80.0, dfm=0.15, zfactor=1.0, callback=None):
- """Reduces short-scale variation in an input DEM while preserving breaks-in-slope and small drainage features using a modified Sun et al. (2007) algorithm.
-
- Keyword arguments:
-
- dem -- Input raster DEM file.
- output -- Output raster file.
- filter -- Size of the filter kernel.
- norm_diff -- Maximum difference in normal vectors, in degrees.
- num_iter -- Number of iterations.
- max_diff -- Maximum allowable absolute elevation change (optional).
- reduction -- Maximum Amount to reduce the threshold angle by (0 = full smoothing; 100 = no smoothing).
- dfm -- Difference from median threshold (in z-units), determines when a location is low-lying.
- zfactor -- Optional multiplier for when the vertical and horizontal units are not the same.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--dem='{}'".format(dem))
- args.append("--output='{}'".format(output))
- args.append("--filter={}".format(filter))
- args.append("--norm_diff={}".format(norm_diff))
- args.append("--num_iter={}".format(num_iter))
- args.append("--max_diff={}".format(max_diff))
- args.append("--reduction={}".format(reduction))
- args.append("--dfm={}".format(dfm))
- args.append("--zfactor={}".format(zfactor))
- return self.run_tool('drainage_preserving_smoothing', args, callback) # returns 1 if error
-
- def edge_density(self, dem, output, filter=11, norm_diff=5.0, zfactor=1.0, callback=None):
- """Calculates the density of edges, or breaks-in-slope within DEMs.
-
- Keyword arguments:
-
- dem -- Input raster DEM file.
- output -- Output raster file.
- filter -- Size of the filter kernel.
- norm_diff -- Maximum difference in normal vectors, in degrees.
- zfactor -- Optional multiplier for when the vertical and horizontal units are not the same.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--dem='{}'".format(dem))
- args.append("--output='{}'".format(output))
- args.append("--filter={}".format(filter))
- args.append("--norm_diff={}".format(norm_diff))
- args.append("--zfactor={}".format(zfactor))
- return self.run_tool('edge_density', args, callback) # returns 1 if error
-
- def elev_above_pit(self, dem, output, callback=None):
- """Calculate the elevation of each grid cell above the nearest downstream pit cell or grid edge cell.
-
- Keyword arguments:
-
- dem -- Input raster DEM file.
- output -- Output raster file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--dem='{}'".format(dem))
- args.append("--output='{}'".format(output))
- return self.run_tool('elev_above_pit', args, callback) # returns 1 if error
-
- def elev_percentile(self, dem, output, filterx=11, filtery=11, sig_digits=2, callback=None):
- """Calculates the elevation percentile raster from a DEM.
-
- Keyword arguments:
-
- dem -- Input raster DEM file.
- output -- Output raster file.
- filterx -- Size of the filter kernel in the x-direction.
- filtery -- Size of the filter kernel in the y-direction.
- sig_digits -- Number of significant digits.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--dem='{}'".format(dem))
- args.append("--output='{}'".format(output))
- args.append("--filterx={}".format(filterx))
- args.append("--filtery={}".format(filtery))
- args.append("--sig_digits={}".format(sig_digits))
- return self.run_tool('elev_percentile', args, callback) # returns 1 if error
-
- def elev_relative_to_min_max(self, dem, output, callback=None):
- """Calculates the elevation of a location relative to the minimum and maximum elevations in a DEM.
-
- Keyword arguments:
-
- dem -- Input raster DEM file.
- output -- Output raster file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--dem='{}'".format(dem))
- args.append("--output='{}'".format(output))
- return self.run_tool('elev_relative_to_min_max', args, callback) # returns 1 if error
-
- def elev_relative_to_watershed_min_max(self, dem, watersheds, output, callback=None):
- """Calculates the elevation of a location relative to the minimum and maximum elevations in a watershed.
-
- Keyword arguments:
-
- dem -- Input raster DEM file.
- watersheds -- Input raster watersheds file.
- output -- Output raster file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--dem='{}'".format(dem))
- args.append("--watersheds='{}'".format(watersheds))
- args.append("--output='{}'".format(output))
- return self.run_tool('elev_relative_to_watershed_min_max', args, callback) # returns 1 if error
-
- def feature_preserving_denoise(self, dem, output, filter=11, norm_diff=15.0, num_iter=3, max_diff=0.5, zfactor=1.0, callback=None):
- """Reduces short-scale variation in an input DEM using a modified Sun et al. (2007) algorithm.
-
- Keyword arguments:
-
- dem -- Input raster DEM file.
- output -- Output raster file.
- filter -- Size of the filter kernel.
- norm_diff -- Maximum difference in normal vectors, in degrees.
- num_iter -- Number of iterations.
- max_diff -- Maximum allowable absolute elevation change (optional).
- zfactor -- Optional multiplier for when the vertical and horizontal units are not the same.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--dem='{}'".format(dem))
- args.append("--output='{}'".format(output))
- args.append("--filter={}".format(filter))
- args.append("--norm_diff={}".format(norm_diff))
- args.append("--num_iter={}".format(num_iter))
- args.append("--max_diff={}".format(max_diff))
- args.append("--zfactor={}".format(zfactor))
- return self.run_tool('feature_preserving_denoise', args, callback) # returns 1 if error
-
- def fetch_analysis(self, dem, output, azimuth=0.0, hgt_inc=0.05, callback=None):
- """Performs an analysis of fetch or upwind distance to an obstacle.
-
- Keyword arguments:
-
- dem -- Input raster DEM file.
- output -- Output raster file.
- azimuth -- Wind azimuth in degrees in degrees.
- hgt_inc -- Height increment value.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--dem='{}'".format(dem))
- args.append("--output='{}'".format(output))
- args.append("--azimuth={}".format(azimuth))
- args.append("--hgt_inc={}".format(hgt_inc))
- return self.run_tool('fetch_analysis', args, callback) # returns 1 if error
-
- def fill_missing_data(self, i, output, filter=11, weight=2.0, callback=None):
- """Fills NoData holes in a DEM.
-
- Keyword arguments:
-
- i -- Input raster file.
- output -- Output raster file.
- filter -- Filter size (cells).
- weight -- IDW weight value.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- args.append("--filter={}".format(filter))
- args.append("--weight={}".format(weight))
- return self.run_tool('fill_missing_data', args, callback) # returns 1 if error
-
- def find_ridges(self, dem, output, line_thin=True, callback=None):
- """Identifies potential ridge and peak grid cells.
-
- Keyword arguments:
-
- dem -- Input raster DEM file.
- output -- Output raster file.
- line_thin -- Optional flag indicating whether post-processing line-thinning should be performed.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--dem='{}'".format(dem))
- args.append("--output='{}'".format(output))
- if line_thin: args.append("--line_thin")
- return self.run_tool('find_ridges', args, callback) # returns 1 if error
-
- def hillshade(self, dem, output, azimuth=315.0, altitude=30.0, zfactor=1.0, callback=None):
- """Calculates a hillshade raster from an input DEM.
-
- Keyword arguments:
-
- dem -- Input raster DEM file.
- output -- Output raster file.
- azimuth -- Illumination source azimuth in degrees.
- altitude -- Illumination source altitude in degrees.
- zfactor -- Optional multiplier for when the vertical and horizontal units are not the same.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--dem='{}'".format(dem))
- args.append("--output='{}'".format(output))
- args.append("--azimuth={}".format(azimuth))
- args.append("--altitude={}".format(altitude))
- args.append("--zfactor={}".format(zfactor))
- return self.run_tool('hillshade', args, callback) # returns 1 if error
-
- def horizon_angle(self, dem, output, azimuth=0.0, max_dist=None, callback=None):
- """Calculates horizon angle (maximum upwind slope) for each grid cell in an input DEM.
-
- Keyword arguments:
-
- dem -- Input raster DEM file.
- output -- Output raster file.
- azimuth -- Wind azimuth in degrees.
- max_dist -- Optional maximum search distance (unspecified if none; in xy units).
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--dem='{}'".format(dem))
- args.append("--output='{}'".format(output))
- args.append("--azimuth={}".format(azimuth))
- if max_dist is not None: args.append("--max_dist='{}'".format(max_dist))
- return self.run_tool('horizon_angle', args, callback) # returns 1 if error
-
- def hypsometric_analysis(self, inputs, output, watershed=None, callback=None):
- """Calculates a hypsometric curve for one or more DEMs.
-
- Keyword arguments:
-
- inputs -- Input DEM files.
- watershed -- Input watershed files (optional).
- output -- Output HTML file (default name will be based on input file if unspecified).
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--inputs='{}'".format(inputs))
- if watershed is not None: args.append("--watershed='{}'".format(watershed))
- args.append("--output='{}'".format(output))
- return self.run_tool('hypsometric_analysis', args, callback) # returns 1 if error
-
- def max_anisotropy_dev(self, dem, out_mag, out_scale, max_scale, min_scale=3, step=2, callback=None):
- """Calculates the maximum anisotropy (directionality) in elevation deviation over a range of spatial scales.
-
- Keyword arguments:
-
- dem -- Input raster DEM file.
- out_mag -- Output raster DEVmax magnitude file.
- out_scale -- Output raster DEVmax scale file.
- min_scale -- Minimum search neighbourhood radius in grid cells.
- max_scale -- Maximum search neighbourhood radius in grid cells.
- step -- Step size as any positive non-zero integer.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--dem='{}'".format(dem))
- args.append("--out_mag='{}'".format(out_mag))
- args.append("--out_scale='{}'".format(out_scale))
- args.append("--min_scale={}".format(min_scale))
- args.append("--max_scale='{}'".format(max_scale))
- args.append("--step={}".format(step))
- return self.run_tool('max_anisotropy_dev', args, callback) # returns 1 if error
-
- def max_anisotropy_dev_signature(self, dem, points, output, max_scale, min_scale=1, step=1, callback=None):
- """Calculates the anisotropy in deviation from mean for points over a range of spatial scales.
-
- Keyword arguments:
-
- dem -- Input raster DEM file.
- points -- Input vector points file.
- output -- Output HTML file.
- min_scale -- Minimum search neighbourhood radius in grid cells.
- max_scale -- Maximum search neighbourhood radius in grid cells.
- step -- Step size as any positive non-zero integer.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--dem='{}'".format(dem))
- args.append("--points='{}'".format(points))
- args.append("--output='{}'".format(output))
- args.append("--min_scale={}".format(min_scale))
- args.append("--max_scale='{}'".format(max_scale))
- args.append("--step={}".format(step))
- return self.run_tool('max_anisotropy_dev_signature', args, callback) # returns 1 if error
-
- def max_branch_length(self, dem, output, log=False, callback=None):
- """Lindsay and Seibert's (2013) branch length index is used to map drainage divides or ridge lines.
-
- Keyword arguments:
-
- dem -- Input raster DEM file.
- output -- Output raster file.
- log -- Optional flag to request the output be log-transformed.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--dem='{}'".format(dem))
- args.append("--output='{}'".format(output))
- if log: args.append("--log")
- return self.run_tool('max_branch_length', args, callback) # returns 1 if error
-
- def max_difference_from_mean(self, dem, out_mag, out_scale, min_scale, max_scale, step=1, callback=None):
- """Calculates the maximum difference from mean elevation over a range of spatial scales.
-
- Keyword arguments:
-
- dem -- Input raster DEM file.
- out_mag -- Output raster DIFFmax magnitude file.
- out_scale -- Output raster DIFFmax scale file.
- min_scale -- Minimum search neighbourhood radius in grid cells.
- max_scale -- Maximum search neighbourhood radius in grid cells.
- step -- Step size as any positive non-zero integer.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--dem='{}'".format(dem))
- args.append("--out_mag='{}'".format(out_mag))
- args.append("--out_scale='{}'".format(out_scale))
- args.append("--min_scale='{}'".format(min_scale))
- args.append("--max_scale='{}'".format(max_scale))
- args.append("--step={}".format(step))
- return self.run_tool('max_difference_from_mean', args, callback) # returns 1 if error
-
- def max_downslope_elev_change(self, dem, output, callback=None):
- """Calculates the maximum downslope change in elevation between a grid cell and its eight downslope neighbors.
-
- Keyword arguments:
-
- dem -- Input raster DEM file.
- output -- Output raster file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--dem='{}'".format(dem))
- args.append("--output='{}'".format(output))
- return self.run_tool('max_downslope_elev_change', args, callback) # returns 1 if error
-
- def max_elev_dev_signature(self, dem, points, output, min_scale, max_scale, step=10, callback=None):
- """Calculates the maximum elevation deviation over a range of spatial scales and for a set of points.
-
- Keyword arguments:
-
- dem -- Input raster DEM file.
- points -- Input vector points file.
- output -- Output HTML file.
- min_scale -- Minimum search neighbourhood radius in grid cells.
- max_scale -- Maximum search neighbourhood radius in grid cells.
- step -- Step size as any positive non-zero integer.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--dem='{}'".format(dem))
- args.append("--points='{}'".format(points))
- args.append("--output='{}'".format(output))
- args.append("--min_scale='{}'".format(min_scale))
- args.append("--max_scale='{}'".format(max_scale))
- args.append("--step={}".format(step))
- return self.run_tool('max_elev_dev_signature', args, callback) # returns 1 if error
-
- def max_elevation_deviation(self, dem, out_mag, out_scale, min_scale, max_scale, step=1, callback=None):
- """Calculates the maximum elevation deviation over a range of spatial scales.
-
- Keyword arguments:
-
- dem -- Input raster DEM file.
- out_mag -- Output raster DEVmax magnitude file.
- out_scale -- Output raster DEVmax scale file.
- min_scale -- Minimum search neighbourhood radius in grid cells.
- max_scale -- Maximum search neighbourhood radius in grid cells.
- step -- Step size as any positive non-zero integer.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--dem='{}'".format(dem))
- args.append("--out_mag='{}'".format(out_mag))
- args.append("--out_scale='{}'".format(out_scale))
- args.append("--min_scale='{}'".format(min_scale))
- args.append("--max_scale='{}'".format(max_scale))
- args.append("--step={}".format(step))
- return self.run_tool('max_elevation_deviation', args, callback) # returns 1 if error
-
- def min_downslope_elev_change(self, dem, output, callback=None):
- """Calculates the minimum downslope change in elevation between a grid cell and its eight downslope neighbors.
-
- Keyword arguments:
-
- dem -- Input raster DEM file.
- output -- Output raster file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--dem='{}'".format(dem))
- args.append("--output='{}'".format(output))
- return self.run_tool('min_downslope_elev_change', args, callback) # returns 1 if error
-
- def multiscale_roughness(self, dem, out_mag, out_scale, max_scale, min_scale=1, step=1, callback=None):
- """Calculates surface roughness over a range of spatial scales.
-
- Keyword arguments:
-
- dem -- Input raster DEM file.
- out_mag -- Output raster roughness magnitude file.
- out_scale -- Output raster roughness scale file.
- min_scale -- Minimum search neighbourhood radius in grid cells.
- max_scale -- Maximum search neighbourhood radius in grid cells.
- step -- Step size as any positive non-zero integer.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--dem='{}'".format(dem))
- args.append("--out_mag='{}'".format(out_mag))
- args.append("--out_scale='{}'".format(out_scale))
- args.append("--min_scale={}".format(min_scale))
- args.append("--max_scale='{}'".format(max_scale))
- args.append("--step={}".format(step))
- return self.run_tool('multiscale_roughness', args, callback) # returns 1 if error
-
- def multiscale_roughness_signature(self, dem, points, output, max_scale, min_scale=1, step=1, callback=None):
- """Calculates the surface roughness for points over a range of spatial scales.
-
- Keyword arguments:
-
- dem -- Input raster DEM file.
- points -- Input vector points file.
- output -- Output HTML file.
- min_scale -- Minimum search neighbourhood radius in grid cells.
- max_scale -- Maximum search neighbourhood radius in grid cells.
- step -- Step size as any positive non-zero integer.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--dem='{}'".format(dem))
- args.append("--points='{}'".format(points))
- args.append("--output='{}'".format(output))
- args.append("--min_scale={}".format(min_scale))
- args.append("--max_scale='{}'".format(max_scale))
- args.append("--step={}".format(step))
- return self.run_tool('multiscale_roughness_signature', args, callback) # returns 1 if error
-
- def multiscale_topographic_position_image(self, local, meso, broad, output, lightness=1.2, callback=None):
- """Creates a multiscale topographic position image from three DEVmax rasters of differing spatial scale ranges.
-
- Keyword arguments:
-
- local -- Input local-scale topographic position (DEVmax) raster file.
- meso -- Input meso-scale topographic position (DEVmax) raster file.
- broad -- Input broad-scale topographic position (DEVmax) raster file.
- output -- Output raster file.
- lightness -- Image lightness value (default is 1.2).
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--local='{}'".format(local))
- args.append("--meso='{}'".format(meso))
- args.append("--broad='{}'".format(broad))
- args.append("--output='{}'".format(output))
- args.append("--lightness={}".format(lightness))
- return self.run_tool('multiscale_topographic_position_image', args, callback) # returns 1 if error
-
- def num_downslope_neighbours(self, dem, output, callback=None):
- """Calculates the number of downslope neighbours to each grid cell in a DEM.
-
- Keyword arguments:
-
- dem -- Input raster DEM file.
- output -- Output raster file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--dem='{}'".format(dem))
- args.append("--output='{}'".format(output))
- return self.run_tool('num_downslope_neighbours', args, callback) # returns 1 if error
-
- def num_upslope_neighbours(self, dem, output, callback=None):
- """Calculates the number of upslope neighbours to each grid cell in a DEM.
-
- Keyword arguments:
-
- dem -- Input raster DEM file.
- output -- Output raster file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--dem='{}'".format(dem))
- args.append("--output='{}'".format(output))
- return self.run_tool('num_upslope_neighbours', args, callback) # returns 1 if error
-
- def pennock_landform_class(self, dem, output, slope=3.0, prof=0.1, plan=0.0, zfactor=1.0, callback=None):
- """Classifies hillslope zones based on slope, profile curvature, and plan curvature.
-
- Keyword arguments:
-
- dem -- Input raster DEM file.
- output -- Output raster file.
- slope -- Slope threshold value, in degrees (default is 3.0).
- prof -- Profile curvature threshold value (default is 0.1).
- plan -- Plan curvature threshold value (default is 0.0).
- zfactor -- Optional multiplier for when the vertical and horizontal units are not the same.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--dem='{}'".format(dem))
- args.append("--output='{}'".format(output))
- args.append("--slope={}".format(slope))
- args.append("--prof={}".format(prof))
- args.append("--plan={}".format(plan))
- args.append("--zfactor={}".format(zfactor))
- return self.run_tool('pennock_landform_class', args, callback) # returns 1 if error
-
- def percent_elev_range(self, dem, output, filterx=3, filtery=3, callback=None):
- """Calculates percent of elevation range from a DEM.
-
- Keyword arguments:
-
- dem -- Input raster DEM file.
- output -- Output raster file.
- filterx -- Size of the filter kernel in the x-direction.
- filtery -- Size of the filter kernel in the y-direction.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--dem='{}'".format(dem))
- args.append("--output='{}'".format(output))
- args.append("--filterx={}".format(filterx))
- args.append("--filtery={}".format(filtery))
- return self.run_tool('percent_elev_range', args, callback) # returns 1 if error
-
- def plan_curvature(self, dem, output, zfactor=1.0, callback=None):
- """Calculates a plan (contour) curvature raster from an input DEM.
-
- Keyword arguments:
-
- dem -- Input raster DEM file.
- output -- Output raster file.
- zfactor -- Optional multiplier for when the vertical and horizontal units are not the same.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--dem='{}'".format(dem))
- args.append("--output='{}'".format(output))
- args.append("--zfactor={}".format(zfactor))
- return self.run_tool('plan_curvature', args, callback) # returns 1 if error
-
- def profile(self, lines, surface, output, callback=None):
- """Plots profiles from digital surface models.
-
- Keyword arguments:
-
- lines -- Input vector line file.
- surface -- Input raster surface file.
- output -- Output HTML file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--lines='{}'".format(lines))
- args.append("--surface='{}'".format(surface))
- args.append("--output='{}'".format(output))
- return self.run_tool('profile', args, callback) # returns 1 if error
-
- def profile_curvature(self, dem, output, zfactor=1.0, callback=None):
- """Calculates a profile curvature raster from an input DEM.
-
- Keyword arguments:
-
- dem -- Input raster DEM file.
- output -- Output raster file.
- zfactor -- Optional multiplier for when the vertical and horizontal units are not the same.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--dem='{}'".format(dem))
- args.append("--output='{}'".format(output))
- args.append("--zfactor={}".format(zfactor))
- return self.run_tool('profile_curvature', args, callback) # returns 1 if error
-
- def relative_aspect(self, dem, output, azimuth=0.0, zfactor=1.0, callback=None):
- """Calculates relative aspect (relative to a user-specified direction) from an input DEM.
-
- Keyword arguments:
-
- dem -- Input raster DEM file.
- output -- Output raster file.
- azimuth -- Illumination source azimuth.
- zfactor -- Optional multiplier for when the vertical and horizontal units are not the same.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--dem='{}'".format(dem))
- args.append("--output='{}'".format(output))
- args.append("--azimuth={}".format(azimuth))
- args.append("--zfactor={}".format(zfactor))
- return self.run_tool('relative_aspect', args, callback) # returns 1 if error
-
- def relative_stream_power_index(self, sca, slope, output, exponent=1.0, callback=None):
- """Calculates the relative stream power index.
-
- Keyword arguments:
-
- sca -- Input raster specific contributing area (SCA) file.
- slope -- Input raster slope file.
- output -- Output raster file.
- exponent -- SCA exponent value.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--sca='{}'".format(sca))
- args.append("--slope='{}'".format(slope))
- args.append("--output='{}'".format(output))
- args.append("--exponent={}".format(exponent))
- return self.run_tool('relative_stream_power_index', args, callback) # returns 1 if error
-
- def relative_topographic_position(self, dem, output, filterx=11, filtery=11, callback=None):
- """Calculates the relative topographic position index from a DEM.
-
- Keyword arguments:
-
- dem -- Input raster DEM file.
- output -- Output raster file.
- filterx -- Size of the filter kernel in the x-direction.
- filtery -- Size of the filter kernel in the y-direction.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--dem='{}'".format(dem))
- args.append("--output='{}'".format(output))
- args.append("--filterx={}".format(filterx))
- args.append("--filtery={}".format(filtery))
- return self.run_tool('relative_topographic_position', args, callback) # returns 1 if error
-
- def remove_off_terrain_objects(self, dem, output, filter=11, slope=15.0, callback=None):
- """Removes off-terrain objects from a raster digital elevation model (DEM).
-
- Keyword arguments:
-
- dem -- Input raster DEM file.
- output -- Output raster file.
- filter -- Filter size (cells).
- slope -- Slope threshold value.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--dem='{}'".format(dem))
- args.append("--output='{}'".format(output))
- args.append("--filter={}".format(filter))
- args.append("--slope={}".format(slope))
- return self.run_tool('remove_off_terrain_objects', args, callback) # returns 1 if error
-
- def ruggedness_index(self, dem, output, zfactor=1.0, callback=None):
- """Calculates the Riley et al.'s (1999) terrain ruggedness index from an input DEM.
-
- Keyword arguments:
-
- dem -- Input raster DEM file.
- output -- Output raster file.
- zfactor -- Optional multiplier for when the vertical and horizontal units are not the same.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--dem='{}'".format(dem))
- args.append("--output='{}'".format(output))
- args.append("--zfactor={}".format(zfactor))
- return self.run_tool('ruggedness_index', args, callback) # returns 1 if error
-
- def sediment_transport_index(self, sca, slope, output, sca_exponent=0.4, slope_exponent=1.3, callback=None):
- """Calculates the sediment transport index.
-
- Keyword arguments:
-
- sca -- Input raster specific contributing area (SCA) file.
- slope -- Input raster slope file.
- output -- Output raster file.
- sca_exponent -- SCA exponent value.
- slope_exponent -- Slope exponent value.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--sca='{}'".format(sca))
- args.append("--slope='{}'".format(slope))
- args.append("--output='{}'".format(output))
- args.append("--sca_exponent={}".format(sca_exponent))
- args.append("--slope_exponent={}".format(slope_exponent))
- return self.run_tool('sediment_transport_index', args, callback) # returns 1 if error
-
- def slope(self, dem, output, zfactor=1.0, callback=None):
- """Calculates a slope raster from an input DEM.
-
- Keyword arguments:
-
- dem -- Input raster DEM file.
- output -- Output raster file.
- zfactor -- Optional multiplier for when the vertical and horizontal units are not the same.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--dem='{}'".format(dem))
- args.append("--output='{}'".format(output))
- args.append("--zfactor={}".format(zfactor))
- return self.run_tool('slope', args, callback) # returns 1 if error
-
- def slope_vs_elevation_plot(self, inputs, output, watershed=None, callback=None):
- """Creates a slope vs. elevation plot for one or more DEMs.
-
- Keyword arguments:
-
- inputs -- Input DEM files.
- watershed -- Input watershed files (optional).
- output -- Output HTML file (default name will be based on input file if unspecified).
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--inputs='{}'".format(inputs))
- if watershed is not None: args.append("--watershed='{}'".format(watershed))
- args.append("--output='{}'".format(output))
- return self.run_tool('slope_vs_elevation_plot', args, callback) # returns 1 if error
-
- def standard_deviation_of_slope(self, i, output, zfactor=1.0, filterx=11, filtery=11, callback=None):
- """Calculates the standard deviation of slope from an input DEM.
-
- Keyword arguments:
-
- i -- Input raster DEM file.
- output -- Output raster DEM file.
- zfactor -- Optional multiplier for when the vertical and horizontal units are not the same.
- filterx -- Size of the filter kernel in the x-direction.
- filtery -- Size of the filter kernel in the y-direction.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- args.append("--zfactor={}".format(zfactor))
- args.append("--filterx={}".format(filterx))
- args.append("--filtery={}".format(filtery))
- return self.run_tool('standard_deviation_of_slope', args, callback) # returns 1 if error
-
- def surface_area_ratio(self, dem, output, callback=None):
- """Calculates a the surface area ratio of each grid cell in an input DEM.
-
- Keyword arguments:
-
- dem -- Input raster DEM file.
- output -- Output raster file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--dem='{}'".format(dem))
- args.append("--output='{}'".format(output))
- return self.run_tool('surface_area_ratio', args, callback) # returns 1 if error
-
- def tangential_curvature(self, dem, output, zfactor=1.0, callback=None):
- """Calculates a tangential curvature raster from an input DEM.
-
- Keyword arguments:
-
- dem -- Input raster DEM file.
- output -- Output raster file.
- zfactor -- Optional multiplier for when the vertical and horizontal units are not the same.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--dem='{}'".format(dem))
- args.append("--output='{}'".format(output))
- args.append("--zfactor={}".format(zfactor))
- return self.run_tool('tangential_curvature', args, callback) # returns 1 if error
-
- def total_curvature(self, dem, output, zfactor=1.0, callback=None):
- """Calculates a total curvature raster from an input DEM.
-
- Keyword arguments:
-
- dem -- Input raster DEM file.
- output -- Output raster file.
- zfactor -- Optional multiplier for when the vertical and horizontal units are not the same.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--dem='{}'".format(dem))
- args.append("--output='{}'".format(output))
- args.append("--zfactor={}".format(zfactor))
- return self.run_tool('total_curvature', args, callback) # returns 1 if error
-
- def viewshed(self, dem, stations, output, height=2.0, callback=None):
- """Identifies the viewshed for a point or set of points.
-
- Keyword arguments:
-
- dem -- Input raster DEM file.
- stations -- Input viewing station vector file.
- output -- Output raster file.
- height -- Viewing station height, in z units.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--dem='{}'".format(dem))
- args.append("--stations='{}'".format(stations))
- args.append("--output='{}'".format(output))
- args.append("--height={}".format(height))
- return self.run_tool('viewshed', args, callback) # returns 1 if error
-
- def visibility_index(self, dem, output, height=2.0, res_factor=2, callback=None):
- """Estimates the relative visibility of sites in a DEM.
-
- Keyword arguments:
-
- dem -- Input raster DEM file.
- output -- Output raster file.
- height -- Viewing station height, in z units.
- res_factor -- The resolution factor determines the density of measured viewsheds.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--dem='{}'".format(dem))
- args.append("--output='{}'".format(output))
- args.append("--height={}".format(height))
- args.append("--res_factor={}".format(res_factor))
- return self.run_tool('visibility_index', args, callback) # returns 1 if error
-
- def wetness_index(self, sca, slope, output, callback=None):
- """Calculates the topographic wetness index, Ln(A / tan(slope)).
-
- Keyword arguments:
-
- sca -- Input raster specific contributing area (SCA) file.
- slope -- Input raster slope file.
- output -- Output raster file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--sca='{}'".format(sca))
- args.append("--slope='{}'".format(slope))
- args.append("--output='{}'".format(output))
- return self.run_tool('wetness_index', args, callback) # returns 1 if error
-
- #########################
- # Hydrological Analysis #
- #########################
-
- def average_flowpath_slope(self, dem, output, callback=None):
- """Measures the average slope gradient from each grid cell to all upslope divide cells.
-
- Keyword arguments:
-
- dem -- Input raster DEM file.
- output -- Output raster file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--dem='{}'".format(dem))
- args.append("--output='{}'".format(output))
- return self.run_tool('average_flowpath_slope', args, callback) # returns 1 if error
-
- def average_upslope_flowpath_length(self, dem, output, callback=None):
- """Measures the average length of all upslope flowpaths draining each grid cell.
-
- Keyword arguments:
-
- dem -- Input raster DEM file.
- output -- Output raster file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--dem='{}'".format(dem))
- args.append("--output='{}'".format(output))
- return self.run_tool('average_upslope_flowpath_length', args, callback) # returns 1 if error
-
- def basins(self, d8_pntr, output, esri_pntr=False, callback=None):
- """Identifies drainage basins that drain to the DEM edge.
-
- Keyword arguments:
-
- d8_pntr -- Input raster D8 pointer file.
- output -- Output raster file.
- esri_pntr -- D8 pointer uses the ESRI style scheme.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--d8_pntr='{}'".format(d8_pntr))
- args.append("--output='{}'".format(output))
- if esri_pntr: args.append("--esri_pntr")
- return self.run_tool('basins', args, callback) # returns 1 if error
-
- def breach_depressions(self, dem, output, max_depth=None, max_length=None, callback=None):
- """Breaches all of the depressions in a DEM using Lindsay's (2016) algorithm. This should be preferred over depression filling in most cases.
-
- Keyword arguments:
-
- dem -- Input raster DEM file.
- output -- Output raster file.
- max_depth -- Optional maximum breach depth (default is Inf).
- max_length -- Optional maximum breach channel length (in grid cells; default is Inf).
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--dem='{}'".format(dem))
- args.append("--output='{}'".format(output))
- if max_depth is not None: args.append("--max_depth='{}'".format(max_depth))
- if max_length is not None: args.append("--max_length='{}'".format(max_length))
- return self.run_tool('breach_depressions', args, callback) # returns 1 if error
-
- def breach_single_cell_pits(self, dem, output, callback=None):
- """Removes single-cell pits from an input DEM by breaching.
-
- Keyword arguments:
-
- dem -- Input raster DEM file.
- output -- Output raster file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--dem='{}'".format(dem))
- args.append("--output='{}'".format(output))
- return self.run_tool('breach_single_cell_pits', args, callback) # returns 1 if error
-
- def d8_flow_accumulation(self, dem, output, out_type="cells", log=False, clip=False, callback=None):
- """Calculates a D8 flow accumulation raster from an input DEM.
-
- Keyword arguments:
-
- dem -- Input raster DEM file.
- output -- Output raster file.
- out_type -- Output type; one of 'cells' (default), 'catchment area', and 'specific contributing area'.
- log -- Optional flag to request the output be log-transformed.
- clip -- Optional flag to request clipping the display max by 1%.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--dem='{}'".format(dem))
- args.append("--output='{}'".format(output))
- args.append("--out_type={}".format(out_type))
- if log: args.append("--log")
- if clip: args.append("--clip")
- return self.run_tool('d8_flow_accumulation', args, callback) # returns 1 if error
-
- def d8_mass_flux(self, dem, loading, efficiency, absorption, output, callback=None):
- """Performs a D8 mass flux calculation.
-
- Keyword arguments:
-
- dem -- Input raster DEM file.
- loading -- Input loading raster file.
- efficiency -- Input efficiency raster file.
- absorption -- Input absorption raster file.
- output -- Output raster file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--dem='{}'".format(dem))
- args.append("--loading='{}'".format(loading))
- args.append("--efficiency='{}'".format(efficiency))
- args.append("--absorption='{}'".format(absorption))
- args.append("--output='{}'".format(output))
- return self.run_tool('d8_mass_flux', args, callback) # returns 1 if error
-
- def d8_pointer(self, dem, output, esri_pntr=False, callback=None):
- """Calculates a D8 flow pointer raster from an input DEM.
-
- Keyword arguments:
-
- dem -- Input raster DEM file.
- output -- Output raster file.
- esri_pntr -- D8 pointer uses the ESRI style scheme.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--dem='{}'".format(dem))
- args.append("--output='{}'".format(output))
- if esri_pntr: args.append("--esri_pntr")
- return self.run_tool('d8_pointer', args, callback) # returns 1 if error
-
- def d_inf_flow_accumulation(self, dem, output, out_type="Specific Contributing Area", threshold=None, log=False, clip=False, callback=None):
- """Calculates a D-infinity flow accumulation raster from an input DEM.
-
- Keyword arguments:
-
- dem -- Input raster DEM file.
- output -- Output raster file.
- out_type -- Output type; one of 'cells', 'sca' (default), and 'ca'.
- threshold -- Optional convergence threshold parameter, in grid cells; default is inifinity.
- log -- Optional flag to request the output be log-transformed.
- clip -- Optional flag to request clipping the display max by 1%.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--dem='{}'".format(dem))
- args.append("--output='{}'".format(output))
- args.append("--out_type={}".format(out_type))
- if threshold is not None: args.append("--threshold='{}'".format(threshold))
- if log: args.append("--log")
- if clip: args.append("--clip")
- return self.run_tool('d_inf_flow_accumulation', args, callback) # returns 1 if error
-
- def d_inf_mass_flux(self, dem, loading, efficiency, absorption, output, callback=None):
- """Performs a D-infinity mass flux calculation.
-
- Keyword arguments:
-
- dem -- Input raster DEM file.
- loading -- Input loading raster file.
- efficiency -- Input efficiency raster file.
- absorption -- Input absorption raster file.
- output -- Output raster file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--dem='{}'".format(dem))
- args.append("--loading='{}'".format(loading))
- args.append("--efficiency='{}'".format(efficiency))
- args.append("--absorption='{}'".format(absorption))
- args.append("--output='{}'".format(output))
- return self.run_tool('d_inf_mass_flux', args, callback) # returns 1 if error
-
- def d_inf_pointer(self, dem, output, callback=None):
- """Calculates a D-infinity flow pointer (flow direction) raster from an input DEM.
-
- Keyword arguments:
-
- dem -- Input raster DEM file.
- output -- Output raster file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--dem='{}'".format(dem))
- args.append("--output='{}'".format(output))
- return self.run_tool('d_inf_pointer', args, callback) # returns 1 if error
-
- def depth_in_sink(self, dem, output, zero_background=False, callback=None):
- """Measures the depth of sinks (depressions) in a DEM.
-
- Keyword arguments:
-
- dem -- Input raster DEM file.
- output -- Output raster file.
- zero_background -- Flag indicating whether the background value of zero should be used.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--dem='{}'".format(dem))
- args.append("--output='{}'".format(output))
- if zero_background: args.append("--zero_background")
- return self.run_tool('depth_in_sink', args, callback) # returns 1 if error
-
- def downslope_distance_to_stream(self, dem, streams, output, callback=None):
- """Measures distance to the nearest downslope stream cell.
-
- Keyword arguments:
-
- dem -- Input raster DEM file.
- streams -- Input raster streams file.
- output -- Output raster file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--dem='{}'".format(dem))
- args.append("--streams='{}'".format(streams))
- args.append("--output='{}'".format(output))
- return self.run_tool('downslope_distance_to_stream', args, callback) # returns 1 if error
-
- def downslope_flowpath_length(self, d8_pntr, output, watersheds=None, weights=None, esri_pntr=False, callback=None):
- """Calculates the downslope flowpath length from each cell to basin outlet.
-
- Keyword arguments:
-
- d8_pntr -- Input D8 pointer raster file.
- watersheds -- Optional input watershed raster file.
- weights -- Optional input weights raster file.
- output -- Output raster file.
- esri_pntr -- D8 pointer uses the ESRI style scheme.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--d8_pntr='{}'".format(d8_pntr))
- if watersheds is not None: args.append("--watersheds='{}'".format(watersheds))
- if weights is not None: args.append("--weights='{}'".format(weights))
- args.append("--output='{}'".format(output))
- if esri_pntr: args.append("--esri_pntr")
- return self.run_tool('downslope_flowpath_length', args, callback) # returns 1 if error
-
- def elevation_above_stream(self, dem, streams, output, callback=None):
- """Calculates the elevation of cells above the nearest downslope stream cell.
-
- Keyword arguments:
-
- dem -- Input raster DEM file.
- streams -- Input raster streams file.
- output -- Output raster file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--dem='{}'".format(dem))
- args.append("--streams='{}'".format(streams))
- args.append("--output='{}'".format(output))
- return self.run_tool('elevation_above_stream', args, callback) # returns 1 if error
-
- def elevation_above_stream_euclidean(self, dem, streams, output, callback=None):
- """Calculates the elevation of cells above the nearest (Euclidean distance) stream cell.
-
- Keyword arguments:
-
- dem -- Input raster DEM file.
- streams -- Input raster streams file.
- output -- Output raster file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--dem='{}'".format(dem))
- args.append("--streams='{}'".format(streams))
- args.append("--output='{}'".format(output))
- return self.run_tool('elevation_above_stream_euclidean', args, callback) # returns 1 if error
-
- def fd8_flow_accumulation(self, dem, output, out_type="specific contributing area", exponent=1.1, threshold=None, log=False, clip=False, callback=None):
- """Calculates an FD8 flow accumulation raster from an input DEM.
-
- Keyword arguments:
-
- dem -- Input raster DEM file.
- output -- Output raster file.
- out_type -- Output type; one of 'cells', 'specific contributing area' (default), and 'catchment area'.
- exponent -- Optional exponent parameter; default is 1.1.
- threshold -- Optional convergence threshold parameter, in grid cells; default is inifinity.
- log -- Optional flag to request the output be log-transformed.
- clip -- Optional flag to request clipping the display max by 1%.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--dem='{}'".format(dem))
- args.append("--output='{}'".format(output))
- args.append("--out_type={}".format(out_type))
- args.append("--exponent={}".format(exponent))
- if threshold is not None: args.append("--threshold='{}'".format(threshold))
- if log: args.append("--log")
- if clip: args.append("--clip")
- return self.run_tool('fd8_flow_accumulation', args, callback) # returns 1 if error
-
- def fd8_pointer(self, dem, output, callback=None):
- """Calculates an FD8 flow pointer raster from an input DEM.
-
- Keyword arguments:
-
- dem -- Input raster DEM file.
- output -- Output raster file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--dem='{}'".format(dem))
- args.append("--output='{}'".format(output))
- return self.run_tool('fd8_pointer', args, callback) # returns 1 if error
-
- def fill_burn(self, dem, streams, output, callback=None):
- """Burns streams into a DEM using the FillBurn (Saunders, 1999) method.
-
- Keyword arguments:
-
- dem -- Input raster DEM file.
- streams -- Input vector streams file.
- output -- Output raster file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--dem='{}'".format(dem))
- args.append("--streams='{}'".format(streams))
- args.append("--output='{}'".format(output))
- return self.run_tool('fill_burn', args, callback) # returns 1 if error
-
- def fill_depressions(self, dem, output, fix_flats=True, callback=None):
- """Fills all of the depressions in a DEM. Depression breaching should be preferred in most cases.
-
- Keyword arguments:
-
- dem -- Input raster DEM file.
- output -- Output raster file.
- fix_flats -- Optional flag indicating whether flat areas should have a small gradient applied.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--dem='{}'".format(dem))
- args.append("--output='{}'".format(output))
- if fix_flats: args.append("--fix_flats")
- return self.run_tool('fill_depressions', args, callback) # returns 1 if error
-
- def fill_single_cell_pits(self, dem, output, callback=None):
- """Raises pit cells to the elevation of their lowest neighbour.
-
- Keyword arguments:
-
- dem -- Input raster DEM file.
- output -- Output raster file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--dem='{}'".format(dem))
- args.append("--output='{}'".format(output))
- return self.run_tool('fill_single_cell_pits', args, callback) # returns 1 if error
-
- def find_no_flow_cells(self, dem, output, callback=None):
- """Finds grid cells with no downslope neighbours.
-
- Keyword arguments:
-
- dem -- Input raster DEM file.
- output -- Output raster file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--dem='{}'".format(dem))
- args.append("--output='{}'".format(output))
- return self.run_tool('find_no_flow_cells', args, callback) # returns 1 if error
-
- def find_parallel_flow(self, d8_pntr, streams, output, callback=None):
- """Finds areas of parallel flow in D8 flow direction rasters.
-
- Keyword arguments:
-
- d8_pntr -- Input D8 pointer raster file.
- streams -- Input raster streams file.
- output -- Output raster file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--d8_pntr='{}'".format(d8_pntr))
- args.append("--streams='{}'".format(streams))
- args.append("--output='{}'".format(output))
- return self.run_tool('find_parallel_flow', args, callback) # returns 1 if error
-
- def flatten_lakes(self, dem, lakes, output, callback=None):
- """Flattens lake polygons in a raster DEM.
-
- Keyword arguments:
-
- dem -- Input raster DEM file.
- lakes -- Input lakes vector polygons file.
- output -- Output raster file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--dem='{}'".format(dem))
- args.append("--lakes='{}'".format(lakes))
- args.append("--output='{}'".format(output))
- return self.run_tool('flatten_lakes', args, callback) # returns 1 if error
-
- def flood_order(self, dem, output, callback=None):
- """Assigns each DEM grid cell its order in the sequence of inundations that are encountered during a search starting from the edges, moving inward at increasing elevations.
-
- Keyword arguments:
-
- dem -- Input raster DEM file.
- output -- Output raster file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--dem='{}'".format(dem))
- args.append("--output='{}'".format(output))
- return self.run_tool('flood_order', args, callback) # returns 1 if error
-
- def flow_accumulation_full_workflow(self, dem, out_dem, out_pntr, out_accum, out_type="Specific Contributing Area", log=False, clip=False, esri_pntr=False, callback=None):
- """Resolves all of the depressions in a DEM, outputting a breached DEM, an aspect-aligned non-divergent flow pointer, and a flow accumulation raster.
-
- Keyword arguments:
-
- dem -- Input raster DEM file.
- out_dem -- Output raster DEM file.
- out_pntr -- Output raster flow pointer file.
- out_accum -- Output raster flow accumulation file.
- out_type -- Output type; one of 'cells', 'sca' (default), and 'ca'.
- log -- Optional flag to request the output be log-transformed.
- clip -- Optional flag to request clipping the display max by 1%.
- esri_pntr -- D8 pointer uses the ESRI style scheme.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--dem='{}'".format(dem))
- args.append("--out_dem='{}'".format(out_dem))
- args.append("--out_pntr='{}'".format(out_pntr))
- args.append("--out_accum='{}'".format(out_accum))
- args.append("--out_type={}".format(out_type))
- if log: args.append("--log")
- if clip: args.append("--clip")
- if esri_pntr: args.append("--esri_pntr")
- return self.run_tool('flow_accumulation_full_workflow', args, callback) # returns 1 if error
-
- def flow_length_diff(self, d8_pntr, output, esri_pntr=False, callback=None):
- """Calculates the local maximum absolute difference in downslope flowpath length, useful in mapping drainage divides and ridges.
-
- Keyword arguments:
-
- d8_pntr -- Input D8 pointer raster file.
- output -- Output raster file.
- esri_pntr -- D8 pointer uses the ESRI style scheme.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--d8_pntr='{}'".format(d8_pntr))
- args.append("--output='{}'".format(output))
- if esri_pntr: args.append("--esri_pntr")
- return self.run_tool('flow_length_diff', args, callback) # returns 1 if error
-
- def hillslopes(self, d8_pntr, streams, output, esri_pntr=False, callback=None):
- """Identifies the individual hillslopes draining to each link in a stream network.
-
- Keyword arguments:
-
- d8_pntr -- Input raster D8 pointer file.
- streams -- Input raster streams file.
- output -- Output raster file.
- esri_pntr -- D8 pointer uses the ESRI style scheme.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--d8_pntr='{}'".format(d8_pntr))
- args.append("--streams='{}'".format(streams))
- args.append("--output='{}'".format(output))
- if esri_pntr: args.append("--esri_pntr")
- return self.run_tool('hillslopes', args, callback) # returns 1 if error
-
- def impoundment_size_index(self, dem, output, damlength, out_type="depth", callback=None):
- """Calculates the impoundment size resulting from damming a DEM.
-
- Keyword arguments:
-
- dem -- Input raster DEM file.
- output -- Output file.
- out_type -- Output type; one of 'depth' (default), 'volume', and 'area'.
- damlength -- Maximum length of thr dam.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--dem='{}'".format(dem))
- args.append("--output='{}'".format(output))
- args.append("--out_type={}".format(out_type))
- args.append("--damlength='{}'".format(damlength))
- return self.run_tool('impoundment_size_index', args, callback) # returns 1 if error
-
- def isobasins(self, dem, output, size, callback=None):
- """Divides a landscape into nearly equal sized drainage basins (i.e. watersheds).
-
- Keyword arguments:
-
- dem -- Input raster DEM file.
- output -- Output raster file.
- size -- Target basin size, in grid cells.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--dem='{}'".format(dem))
- args.append("--output='{}'".format(output))
- args.append("--size='{}'".format(size))
- return self.run_tool('isobasins', args, callback) # returns 1 if error
-
- def jenson_snap_pour_points(self, pour_pts, streams, output, snap_dist, callback=None):
- """Moves outlet points used to specify points of interest in a watershedding operation to the nearest stream cell.
-
- Keyword arguments:
-
- pour_pts -- Input vector pour points (outlet) file.
- streams -- Input raster streams file.
- output -- Output vector file.
- snap_dist -- Maximum snap distance in map units.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--pour_pts='{}'".format(pour_pts))
- args.append("--streams='{}'".format(streams))
- args.append("--output='{}'".format(output))
- args.append("--snap_dist='{}'".format(snap_dist))
- return self.run_tool('jenson_snap_pour_points', args, callback) # returns 1 if error
-
- def longest_flowpath(self, dem, basins, output, callback=None):
- """Delineates the longest flowpaths for a group of subbasins or watersheds.
-
- Keyword arguments:
-
- dem -- Input raster DEM file.
- basins -- Input raster basins file.
- output -- Output vector file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--dem='{}'".format(dem))
- args.append("--basins='{}'".format(basins))
- args.append("--output='{}'".format(output))
- return self.run_tool('longest_flowpath', args, callback) # returns 1 if error
-
- def max_upslope_flowpath_length(self, dem, output, callback=None):
- """Measures the maximum length of all upslope flowpaths draining each grid cell.
-
- Keyword arguments:
-
- dem -- Input raster DEM file.
- output -- Output raster file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--dem='{}'".format(dem))
- args.append("--output='{}'".format(output))
- return self.run_tool('max_upslope_flowpath_length', args, callback) # returns 1 if error
-
- def num_inflowing_neighbours(self, dem, output, callback=None):
- """Computes the number of inflowing neighbours to each cell in an input DEM based on the D8 algorithm.
-
- Keyword arguments:
-
- dem -- Input raster DEM file.
- output -- Output raster file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--dem='{}'".format(dem))
- args.append("--output='{}'".format(output))
- return self.run_tool('num_inflowing_neighbours', args, callback) # returns 1 if error
-
- def raise_walls(self, i, dem, output, breach=None, height=100.0, callback=None):
- """Raises walls in a DEM along a line or around a polygon, e.g. a watershed.
-
- Keyword arguments:
-
- i -- Input vector lines or polygons file.
- breach -- Optional input vector breach lines.
- dem -- Input raster DEM file.
- output -- Output raster file.
- height -- Wall height.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- if breach is not None: args.append("--breach='{}'".format(breach))
- args.append("--dem='{}'".format(dem))
- args.append("--output='{}'".format(output))
- args.append("--height={}".format(height))
- return self.run_tool('raise_walls', args, callback) # returns 1 if error
-
- def rho8_pointer(self, dem, output, esri_pntr=False, callback=None):
- """Calculates a stochastic Rho8 flow pointer raster from an input DEM.
-
- Keyword arguments:
-
- dem -- Input raster DEM file.
- output -- Output raster file.
- esri_pntr -- D8 pointer uses the ESRI style scheme.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--dem='{}'".format(dem))
- args.append("--output='{}'".format(output))
- if esri_pntr: args.append("--esri_pntr")
- return self.run_tool('rho8_pointer', args, callback) # returns 1 if error
-
- def sink(self, dem, output, zero_background=False, callback=None):
- """Identifies the depressions in a DEM, giving each feature a unique identifier.
-
- Keyword arguments:
-
- dem -- Input raster DEM file.
- output -- Output raster file.
- zero_background -- Flag indicating whether a background value of zero should be used.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--dem='{}'".format(dem))
- args.append("--output='{}'".format(output))
- if zero_background: args.append("--zero_background")
- return self.run_tool('sink', args, callback) # returns 1 if error
-
- def snap_pour_points(self, pour_pts, flow_accum, output, snap_dist, callback=None):
- """Moves outlet points used to specify points of interest in a watershedding operation to the cell with the highest flow accumulation in its neighbourhood.
-
- Keyword arguments:
-
- pour_pts -- Input vector pour points (outlet) file.
- flow_accum -- Input raster D8 flow accumulation file.
- output -- Output vector file.
- snap_dist -- Maximum snap distance in map units.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--pour_pts='{}'".format(pour_pts))
- args.append("--flow_accum='{}'".format(flow_accum))
- args.append("--output='{}'".format(output))
- args.append("--snap_dist='{}'".format(snap_dist))
- return self.run_tool('snap_pour_points', args, callback) # returns 1 if error
-
- def stochastic_depression_analysis(self, dem, output, rmse, range, iterations=1000, callback=None):
- """Preforms a stochastic analysis of depressions within a DEM.
-
- Keyword arguments:
-
- dem -- Input raster DEM file.
- output -- Output file.
- rmse -- The DEM's root-mean-square-error (RMSE), in z units. This determines error magnitude.
- range -- The error field's correlation length, in xy-units.
- iterations -- The number of iterations.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--dem='{}'".format(dem))
- args.append("--output='{}'".format(output))
- args.append("--rmse='{}'".format(rmse))
- args.append("--range='{}'".format(range))
- args.append("--iterations={}".format(iterations))
- return self.run_tool('stochastic_depression_analysis', args, callback) # returns 1 if error
-
- def strahler_order_basins(self, d8_pntr, streams, output, esri_pntr=False, callback=None):
- """Identifies Strahler-order basins from an input stream network.
-
- Keyword arguments:
-
- d8_pntr -- Input raster D8 pointer file.
- streams -- Input raster streams file.
- output -- Output raster file.
- esri_pntr -- D8 pointer uses the ESRI style scheme.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--d8_pntr='{}'".format(d8_pntr))
- args.append("--streams='{}'".format(streams))
- args.append("--output='{}'".format(output))
- if esri_pntr: args.append("--esri_pntr")
- return self.run_tool('strahler_order_basins', args, callback) # returns 1 if error
-
- def subbasins(self, d8_pntr, streams, output, esri_pntr=False, callback=None):
- """Identifies the catchments, or sub-basin, draining to each link in a stream network.
-
- Keyword arguments:
-
- d8_pntr -- Input D8 pointer raster file.
- streams -- Input raster streams file.
- output -- Output raster file.
- esri_pntr -- D8 pointer uses the ESRI style scheme.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--d8_pntr='{}'".format(d8_pntr))
- args.append("--streams='{}'".format(streams))
- args.append("--output='{}'".format(output))
- if esri_pntr: args.append("--esri_pntr")
- return self.run_tool('subbasins', args, callback) # returns 1 if error
-
- def trace_downslope_flowpaths(self, seed_pts, d8_pntr, output, esri_pntr=False, zero_background=False, callback=None):
- """Traces downslope flowpaths from one or more target sites (i.e. seed points).
-
- Keyword arguments:
-
- seed_pts -- Input vector seed points file.
- d8_pntr -- Input D8 pointer raster file.
- output -- Output raster file.
- esri_pntr -- D8 pointer uses the ESRI style scheme.
- zero_background -- Flag indicating whether a background value of zero should be used.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--seed_pts='{}'".format(seed_pts))
- args.append("--d8_pntr='{}'".format(d8_pntr))
- args.append("--output='{}'".format(output))
- if esri_pntr: args.append("--esri_pntr")
- if zero_background: args.append("--zero_background")
- return self.run_tool('trace_downslope_flowpaths', args, callback) # returns 1 if error
-
- def unnest_basins(self, d8_pntr, pour_pts, output, esri_pntr=False, callback=None):
- """Extract whole watersheds for a set of outlet points.
-
- Keyword arguments:
-
- d8_pntr -- Input D8 pointer raster file.
- pour_pts -- Input vector pour points (outlet) file.
- output -- Output raster file.
- esri_pntr -- D8 pointer uses the ESRI style scheme.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--d8_pntr='{}'".format(d8_pntr))
- args.append("--pour_pts='{}'".format(pour_pts))
- args.append("--output='{}'".format(output))
- if esri_pntr: args.append("--esri_pntr")
- return self.run_tool('unnest_basins', args, callback) # returns 1 if error
-
- def watershed(self, d8_pntr, pour_pts, output, esri_pntr=False, callback=None):
- """Identifies the watershed, or drainage basin, draining to a set of target cells.
-
- Keyword arguments:
-
- d8_pntr -- Input D8 pointer raster file.
- pour_pts -- Input vector pour points (outlet) file.
- output -- Output raster file.
- esri_pntr -- D8 pointer uses the ESRI style scheme.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--d8_pntr='{}'".format(d8_pntr))
- args.append("--pour_pts='{}'".format(pour_pts))
- args.append("--output='{}'".format(output))
- if esri_pntr: args.append("--esri_pntr")
- return self.run_tool('watershed', args, callback) # returns 1 if error
-
- ##########################
- # Image Processing Tools #
- ##########################
-
- def change_vector_analysis(self, date1, date2, magnitude, direction, callback=None):
- """Performs a change vector analysis on a two-date multi-spectral dataset.
-
- Keyword arguments:
-
- date1 -- Input raster files for the earlier date.
- date2 -- Input raster files for the later date.
- magnitude -- Output vector magnitude raster file.
- direction -- Output vector Direction raster file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--date1='{}'".format(date1))
- args.append("--date2='{}'".format(date2))
- args.append("--magnitude='{}'".format(magnitude))
- args.append("--direction='{}'".format(direction))
- return self.run_tool('change_vector_analysis', args, callback) # returns 1 if error
-
- def closing(self, i, output, filterx=11, filtery=11, callback=None):
- """A closing is a mathematical morphology operation involving an erosion (min filter) of a dilation (max filter) set.
-
- Keyword arguments:
-
- i -- Input raster file.
- output -- Output raster file.
- filterx -- Size of the filter kernel in the x-direction.
- filtery -- Size of the filter kernel in the y-direction.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- args.append("--filterx={}".format(filterx))
- args.append("--filtery={}".format(filtery))
- return self.run_tool('closing', args, callback) # returns 1 if error
-
- def create_colour_composite(self, red, green, blue, output, opacity=None, enhance=True, zeros=False, callback=None):
- """Creates a colour-composite image from three bands of multispectral imagery.
-
- Keyword arguments:
-
- red -- Input red band image file.
- green -- Input green band image file.
- blue -- Input blue band image file.
- opacity -- Input opacity band image file (optional).
- output -- Output colour composite file.
- enhance -- Optional flag indicating whether a balance contrast enhancement is performed.
- zeros -- Optional flag to indicate if zeros are nodata values.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--red='{}'".format(red))
- args.append("--green='{}'".format(green))
- args.append("--blue='{}'".format(blue))
- if opacity is not None: args.append("--opacity='{}'".format(opacity))
- args.append("--output='{}'".format(output))
- if enhance: args.append("--enhance")
- if zeros: args.append("--zeros")
- return self.run_tool('create_colour_composite', args, callback) # returns 1 if error
-
- def flip_image(self, i, output, direction="vertical", callback=None):
- """Reflects an image in the vertical or horizontal axis.
-
- Keyword arguments:
-
- i -- Input raster file.
- output -- Output raster file.
- direction -- Direction of reflection; options include 'v' (vertical), 'h' (horizontal), and 'b' (both).
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- args.append("--direction={}".format(direction))
- return self.run_tool('flip_image', args, callback) # returns 1 if error
-
- def ihs_to_rgb(self, intensity, hue, saturation, red=None, green=None, blue=None, output=None, callback=None):
- """Converts intensity, hue, and saturation (IHS) images into red, green, and blue (RGB) images.
-
- Keyword arguments:
-
- intensity -- Input intensity file.
- hue -- Input hue file.
- saturation -- Input saturation file.
- red -- Output red band file. Optionally specified if colour-composite not specified.
- green -- Output green band file. Optionally specified if colour-composite not specified.
- blue -- Output blue band file. Optionally specified if colour-composite not specified.
- output -- Output colour-composite file. Only used if individual bands are not specified.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--intensity='{}'".format(intensity))
- args.append("--hue='{}'".format(hue))
- args.append("--saturation='{}'".format(saturation))
- if red is not None: args.append("--red='{}'".format(red))
- if green is not None: args.append("--green='{}'".format(green))
- if blue is not None: args.append("--blue='{}'".format(blue))
- if output is not None: args.append("--output='{}'".format(output))
- return self.run_tool('ihs_to_rgb', args, callback) # returns 1 if error
-
- def image_stack_profile(self, inputs, points, output, callback=None):
- """Plots an image stack profile (i.e. signature) for a set of points and multispectral images.
-
- Keyword arguments:
-
- inputs -- Input multispectral image files.
- points -- Input vector points file.
- output -- Output HTML file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--inputs='{}'".format(inputs))
- args.append("--points='{}'".format(points))
- args.append("--output='{}'".format(output))
- return self.run_tool('image_stack_profile', args, callback) # returns 1 if error
-
- def integral_image(self, i, output, callback=None):
- """Transforms an input image (summed area table) into its integral image equivalent.
-
- Keyword arguments:
-
- i -- Input raster file.
- output -- Output raster file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- return self.run_tool('integral_image', args, callback) # returns 1 if error
-
- def k_means_clustering(self, inputs, output, classes, out_html=None, max_iterations=10, class_change=2.0, initialize="diagonal", min_class_size=10, callback=None):
- """Performs a k-means clustering operation on a multi-spectral dataset.
-
- Keyword arguments:
-
- inputs -- Input raster files.
- output -- Output raster file.
- out_html -- Output HTML report file.
- classes -- Number of classes.
- max_iterations -- Maximum number of iterations.
- class_change -- Minimum percent of cells changed between iterations before completion.
- initialize -- How to initialize cluster centres?.
- min_class_size -- Minimum class size, in pixels.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--inputs='{}'".format(inputs))
- args.append("--output='{}'".format(output))
- if out_html is not None: args.append("--out_html='{}'".format(out_html))
- args.append("--classes='{}'".format(classes))
- args.append("--max_iterations={}".format(max_iterations))
- args.append("--class_change={}".format(class_change))
- args.append("--initialize={}".format(initialize))
- args.append("--min_class_size={}".format(min_class_size))
- return self.run_tool('k_means_clustering', args, callback) # returns 1 if error
-
- def line_thinning(self, i, output, callback=None):
- """Performs line thinning a on Boolean raster image; intended to be used with the RemoveSpurs tool.
-
- Keyword arguments:
-
- i -- Input raster file.
- output -- Output raster file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- return self.run_tool('line_thinning', args, callback) # returns 1 if error
-
- def modified_k_means_clustering(self, inputs, output, out_html=None, start_clusters=1000, merger_dist=None, max_iterations=10, class_change=2.0, callback=None):
- """Performs a modified k-means clustering operation on a multi-spectral dataset.
-
- Keyword arguments:
-
- inputs -- Input raster files.
- output -- Output raster file.
- out_html -- Output HTML report file.
- start_clusters -- Initial number of clusters.
- merger_dist -- Cluster merger distance.
- max_iterations -- Maximum number of iterations.
- class_change -- Minimum percent of cells changed between iterations before completion.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--inputs='{}'".format(inputs))
- args.append("--output='{}'".format(output))
- if out_html is not None: args.append("--out_html='{}'".format(out_html))
- args.append("--start_clusters={}".format(start_clusters))
- if merger_dist is not None: args.append("--merger_dist='{}'".format(merger_dist))
- args.append("--max_iterations={}".format(max_iterations))
- args.append("--class_change={}".format(class_change))
- return self.run_tool('modified_k_means_clustering', args, callback) # returns 1 if error
-
- def mosaic(self, inputs, output, method="cc", callback=None):
- """Mosaics two or more images together.
-
- Keyword arguments:
-
- inputs -- Input raster files.
- output -- Output raster file.
- method -- Resampling method; options include 'nn' (nearest neighbour), 'bilinear', and 'cc' (cubic convolution).
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--inputs='{}'".format(inputs))
- args.append("--output='{}'".format(output))
- args.append("--method={}".format(method))
- return self.run_tool('mosaic', args, callback) # returns 1 if error
-
- def mosaic_with_feathering(self, input1, input2, output, method="cc", weight=4.0, callback=None):
- """Mosaics two images together using a feathering technique in overlapping areas to reduce edge-effects.
-
- Keyword arguments:
-
- input1 -- Input raster file to modify.
- input2 -- Input reference raster file.
- output -- Output raster file.
- method -- Resampling method; options include 'nn' (nearest neighbour), 'bilinear', and 'cc' (cubic convolution).
- weight -- .
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input1='{}'".format(input1))
- args.append("--input2='{}'".format(input2))
- args.append("--output='{}'".format(output))
- args.append("--method={}".format(method))
- args.append("--weight={}".format(weight))
- return self.run_tool('mosaic_with_feathering', args, callback) # returns 1 if error
-
- def normalized_difference_vegetation_index(self, nir, red, output, clip=0.0, osavi=False, callback=None):
- """Calculates the normalized difference vegetation index (NDVI) from near-infrared and red imagery.
-
- Keyword arguments:
-
- nir -- Input near-infrared band image.
- red -- Input red band image.
- output -- Output raster file.
- clip -- Optional amount to clip the distribution tails by, in percent.
- osavi -- Optional flag indicating whether the optimized soil-adjusted veg index (OSAVI) should be used.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--nir='{}'".format(nir))
- args.append("--red='{}'".format(red))
- args.append("--output='{}'".format(output))
- args.append("--clip={}".format(clip))
- if osavi: args.append("--osavi")
- return self.run_tool('normalized_difference_vegetation_index', args, callback) # returns 1 if error
-
- def opening(self, i, output, filterx=11, filtery=11, callback=None):
- """An opening is a mathematical morphology operation involving a dilation (max filter) of an erosion (min filter) set.
-
- Keyword arguments:
-
- i -- Input raster file.
- output -- Output raster file.
- filterx -- Size of the filter kernel in the x-direction.
- filtery -- Size of the filter kernel in the y-direction.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- args.append("--filterx={}".format(filterx))
- args.append("--filtery={}".format(filtery))
- return self.run_tool('opening', args, callback) # returns 1 if error
-
- def remove_spurs(self, i, output, iterations=10, callback=None):
- """Removes the spurs (pruning operation) from a Boolean line image; intended to be used on the output of the LineThinning tool.
-
- Keyword arguments:
-
- i -- Input raster file.
- output -- Output raster file.
- iterations -- Maximum number of iterations.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- args.append("--iterations={}".format(iterations))
- return self.run_tool('remove_spurs', args, callback) # returns 1 if error
-
- def resample(self, inputs, destination, method="cc", callback=None):
- """Resamples one or more input images into a destination image.
-
- Keyword arguments:
-
- inputs -- Input raster files.
- destination -- Destination raster file.
- method -- Resampling method; options include 'nn' (nearest neighbour), 'bilinear', and 'cc' (cubic convolution).
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--inputs='{}'".format(inputs))
- args.append("--destination='{}'".format(destination))
- args.append("--method={}".format(method))
- return self.run_tool('resample', args, callback) # returns 1 if error
-
- def rgb_to_ihs(self, intensity, hue, saturation, red=None, green=None, blue=None, composite=None, callback=None):
- """Converts red, green, and blue (RGB) images into intensity, hue, and saturation (IHS) images.
-
- Keyword arguments:
-
- red -- Input red band image file. Optionally specified if colour-composite not specified.
- green -- Input green band image file. Optionally specified if colour-composite not specified.
- blue -- Input blue band image file. Optionally specified if colour-composite not specified.
- composite -- Input colour-composite image file. Only used if individual bands are not specified.
- intensity -- Output intensity raster file.
- hue -- Output hue raster file.
- saturation -- Output saturation raster file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- if red is not None: args.append("--red='{}'".format(red))
- if green is not None: args.append("--green='{}'".format(green))
- if blue is not None: args.append("--blue='{}'".format(blue))
- if composite is not None: args.append("--composite='{}'".format(composite))
- args.append("--intensity='{}'".format(intensity))
- args.append("--hue='{}'".format(hue))
- args.append("--saturation='{}'".format(saturation))
- return self.run_tool('rgb_to_ihs', args, callback) # returns 1 if error
-
- def split_colour_composite(self, i, output, callback=None):
- """This tool splits an RGB colour composite image into seperate multispectral images.
-
- Keyword arguments:
-
- i -- Input colour composite image file.
- output -- Output raster file (suffixes of _r, _g, and _b will be appended).
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- return self.run_tool('split_colour_composite', args, callback) # returns 1 if error
-
- def thicken_raster_line(self, i, output, callback=None):
- """Thickens single-cell wide lines within a raster image.
-
- Keyword arguments:
-
- i -- Input raster file.
- output -- Output raster file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- return self.run_tool('thicken_raster_line', args, callback) # returns 1 if error
-
- def tophat_transform(self, i, output, filterx=11, filtery=11, variant="white", callback=None):
- """Performs either a white or black top-hat transform on an input image.
-
- Keyword arguments:
-
- i -- Input raster file.
- output -- Output raster file.
- filterx -- Size of the filter kernel in the x-direction.
- filtery -- Size of the filter kernel in the y-direction.
- variant -- Optional variant value. Options include 'white' and 'black'.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- args.append("--filterx={}".format(filterx))
- args.append("--filtery={}".format(filtery))
- args.append("--variant={}".format(variant))
- return self.run_tool('tophat_transform', args, callback) # returns 1 if error
-
- def write_function_memory_insertion(self, input1, input2, output, input3=None, callback=None):
- """Performs a write function memory insertion for single-band multi-date change detection.
-
- Keyword arguments:
-
- input1 -- Input raster file associated with the first date.
- input2 -- Input raster file associated with the second date.
- input3 -- Optional input raster file associated with the third date.
- output -- Output raster file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input1='{}'".format(input1))
- args.append("--input2='{}'".format(input2))
- if input3 is not None: args.append("--input3='{}'".format(input3))
- args.append("--output='{}'".format(output))
- return self.run_tool('write_function_memory_insertion', args, callback) # returns 1 if error
-
- ##################################
- # Image Processing Tools/Filters #
- ##################################
-
- def adaptive_filter(self, i, output, filterx=11, filtery=11, threshold=2.0, callback=None):
- """Performs an adaptive filter on an image.
-
- Keyword arguments:
-
- i -- Input raster file.
- output -- Output raster file.
- filterx -- Size of the filter kernel in the x-direction.
- filtery -- Size of the filter kernel in the y-direction.
- threshold -- Difference from mean threshold, in standard deviations.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- args.append("--filterx={}".format(filterx))
- args.append("--filtery={}".format(filtery))
- args.append("--threshold={}".format(threshold))
- return self.run_tool('adaptive_filter', args, callback) # returns 1 if error
-
- def bilateral_filter(self, i, output, sigma_dist=0.75, sigma_int=1.0, callback=None):
- """A bilateral filter is an edge-preserving smoothing filter introduced by Tomasi and Manduchi (1998).
-
- Keyword arguments:
-
- i -- Input raster file.
- output -- Output raster file.
- sigma_dist -- Standard deviation in distance in pixels.
- sigma_int -- Standard deviation in intensity in pixels.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- args.append("--sigma_dist={}".format(sigma_dist))
- args.append("--sigma_int={}".format(sigma_int))
- return self.run_tool('bilateral_filter', args, callback) # returns 1 if error
-
- def conservative_smoothing_filter(self, i, output, filterx=3, filtery=3, callback=None):
- """Performs a conservative-smoothing filter on an image.
-
- Keyword arguments:
-
- i -- Input raster file.
- output -- Output raster file.
- filterx -- Size of the filter kernel in the x-direction.
- filtery -- Size of the filter kernel in the y-direction.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- args.append("--filterx={}".format(filterx))
- args.append("--filtery={}".format(filtery))
- return self.run_tool('conservative_smoothing_filter', args, callback) # returns 1 if error
-
- def corner_detection(self, i, output, callback=None):
- """Identifies corner patterns in boolean images using hit-and-miss pattern mattching.
-
- Keyword arguments:
-
- i -- Input boolean image.
- output -- Output raster file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- return self.run_tool('corner_detection', args, callback) # returns 1 if error
-
- def diff_of_gaussian_filter(self, i, output, sigma1=2.0, sigma2=4.0, callback=None):
- """Performs a Difference of Gaussian (DoG) filter on an image.
-
- Keyword arguments:
-
- i -- Input raster file.
- output -- Output raster file.
- sigma1 -- Standard deviation distance in pixels.
- sigma2 -- Standard deviation distance in pixels.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- args.append("--sigma1={}".format(sigma1))
- args.append("--sigma2={}".format(sigma2))
- return self.run_tool('diff_of_gaussian_filter', args, callback) # returns 1 if error
-
- def diversity_filter(self, i, output, filterx=11, filtery=11, callback=None):
- """Assigns each cell in the output grid the number of different values in a moving window centred on each grid cell in the input raster.
-
- Keyword arguments:
-
- i -- Input raster file.
- output -- Output raster file.
- filterx -- Size of the filter kernel in the x-direction.
- filtery -- Size of the filter kernel in the y-direction.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- args.append("--filterx={}".format(filterx))
- args.append("--filtery={}".format(filtery))
- return self.run_tool('diversity_filter', args, callback) # returns 1 if error
-
- def edge_preserving_mean_filter(self, i, output, threshold, filter=11, callback=None):
- """Performs a simple edge-preserving mean filter on an input image.
-
- Keyword arguments:
-
- i -- Input raster file.
- output -- Output raster file.
- filter -- Size of the filter kernel.
- threshold -- Maximum difference in values.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- args.append("--filter={}".format(filter))
- args.append("--threshold='{}'".format(threshold))
- return self.run_tool('edge_preserving_mean_filter', args, callback) # returns 1 if error
-
- def emboss_filter(self, i, output, direction="n", clip=0.0, callback=None):
- """Performs an emboss filter on an image, similar to a hillshade operation.
-
- Keyword arguments:
-
- i -- Input raster file.
- output -- Output raster file.
- direction -- Direction of reflection; options include 'n', 's', 'e', 'w', 'ne', 'se', 'nw', 'sw'.
- clip -- Optional amount to clip the distribution tails by, in percent.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- args.append("--direction={}".format(direction))
- args.append("--clip={}".format(clip))
- return self.run_tool('emboss_filter', args, callback) # returns 1 if error
-
- def fast_almost_gaussian_filter(self, i, output, sigma=1.8, callback=None):
- """Performs a fast approximate Gaussian filter on an image.
-
- Keyword arguments:
-
- i -- Input raster file.
- output -- Output raster file.
- sigma -- Standard deviation distance in pixels.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- args.append("--sigma={}".format(sigma))
- return self.run_tool('fast_almost_gaussian_filter', args, callback) # returns 1 if error
-
- def gaussian_filter(self, i, output, sigma=0.75, callback=None):
- """Performs a Gaussian filter on an image.
-
- Keyword arguments:
-
- i -- Input raster file.
- output -- Output raster file.
- sigma -- Standard deviation distance in pixels.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- args.append("--sigma={}".format(sigma))
- return self.run_tool('gaussian_filter', args, callback) # returns 1 if error
-
- def high_pass_filter(self, i, output, filterx=11, filtery=11, callback=None):
- """Performs a high-pass filter on an input image.
-
- Keyword arguments:
-
- i -- Input raster file.
- output -- Output raster file.
- filterx -- Size of the filter kernel in the x-direction.
- filtery -- Size of the filter kernel in the y-direction.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- args.append("--filterx={}".format(filterx))
- args.append("--filtery={}".format(filtery))
- return self.run_tool('high_pass_filter', args, callback) # returns 1 if error
-
- def high_pass_median_filter(self, i, output, filterx=11, filtery=11, sig_digits=2, callback=None):
- """Performs a high pass median filter on an input image.
-
- Keyword arguments:
-
- i -- Input raster file.
- output -- Output raster file.
- filterx -- Size of the filter kernel in the x-direction.
- filtery -- Size of the filter kernel in the y-direction.
- sig_digits -- Number of significant digits.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- args.append("--filterx={}".format(filterx))
- args.append("--filtery={}".format(filtery))
- args.append("--sig_digits={}".format(sig_digits))
- return self.run_tool('high_pass_median_filter', args, callback) # returns 1 if error
-
- def k_nearest_mean_filter(self, i, output, filterx=11, filtery=11, k=5, callback=None):
- """A k-nearest mean filter is a type of edge-preserving smoothing filter.
-
- Keyword arguments:
-
- i -- Input raster file.
- output -- Output raster file.
- filterx -- Size of the filter kernel in the x-direction.
- filtery -- Size of the filter kernel in the y-direction.
- k -- k-value in pixels; this is the number of nearest-valued neighbours to use.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- args.append("--filterx={}".format(filterx))
- args.append("--filtery={}".format(filtery))
- args.append("-k={}".format(k))
- return self.run_tool('k_nearest_mean_filter', args, callback) # returns 1 if error
-
- def laplacian_filter(self, i, output, variant="3x3(1)", clip=0.0, callback=None):
- """Performs a Laplacian filter on an image.
-
- Keyword arguments:
-
- i -- Input raster file.
- output -- Output raster file.
- variant -- Optional variant value. Options include 3x3(1), 3x3(2), 3x3(3), 3x3(4), 5x5(1), and 5x5(2) (default is 3x3(1)).
- clip -- Optional amount to clip the distribution tails by, in percent.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- args.append("--variant={}".format(variant))
- args.append("--clip={}".format(clip))
- return self.run_tool('laplacian_filter', args, callback) # returns 1 if error
-
- def laplacian_of_gaussian_filter(self, i, output, sigma=0.75, callback=None):
- """Performs a Laplacian-of-Gaussian (LoG) filter on an image.
-
- Keyword arguments:
-
- i -- Input raster file.
- output -- Output raster file.
- sigma -- Standard deviation in pixels.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- args.append("--sigma={}".format(sigma))
- return self.run_tool('laplacian_of_gaussian_filter', args, callback) # returns 1 if error
-
- def lee_filter(self, i, output, filterx=11, filtery=11, sigma=10.0, m=5.0, callback=None):
- """Performs a Lee (Sigma) smoothing filter on an image.
-
- Keyword arguments:
-
- i -- Input raster file.
- output -- Output raster file.
- filterx -- Size of the filter kernel in the x-direction.
- filtery -- Size of the filter kernel in the y-direction.
- sigma -- Sigma value should be related to the standarad deviation of the distribution of image speckle noise.
- m -- M-threshold value the minimum allowable number of pixels within the intensity range.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- args.append("--filterx={}".format(filterx))
- args.append("--filtery={}".format(filtery))
- args.append("--sigma={}".format(sigma))
- args.append("-m={}".format(m))
- return self.run_tool('lee_filter', args, callback) # returns 1 if error
-
- def line_detection_filter(self, i, output, variant="vertical", absvals=False, clip=0.0, callback=None):
- """Performs a line-detection filter on an image.
-
- Keyword arguments:
-
- i -- Input raster file.
- output -- Output raster file.
- variant -- Optional variant value. Options include 'v' (vertical), 'h' (horizontal), '45', and '135' (default is 'v').
- absvals -- Optional flag indicating whether outputs should be absolute values.
- clip -- Optional amount to clip the distribution tails by, in percent.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- args.append("--variant={}".format(variant))
- if absvals: args.append("--absvals")
- args.append("--clip={}".format(clip))
- return self.run_tool('line_detection_filter', args, callback) # returns 1 if error
-
- def majority_filter(self, i, output, filterx=11, filtery=11, callback=None):
- """Assigns each cell in the output grid the most frequently occurring value (mode) in a moving window centred on each grid cell in the input raster.
-
- Keyword arguments:
-
- i -- Input raster file.
- output -- Output raster file.
- filterx -- Size of the filter kernel in the x-direction.
- filtery -- Size of the filter kernel in the y-direction.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- args.append("--filterx={}".format(filterx))
- args.append("--filtery={}".format(filtery))
- return self.run_tool('majority_filter', args, callback) # returns 1 if error
-
- def maximum_filter(self, i, output, filterx=11, filtery=11, callback=None):
- """Assigns each cell in the output grid the maximum value in a moving window centred on each grid cell in the input raster.
-
- Keyword arguments:
-
- i -- Input raster file.
- output -- Output raster file.
- filterx -- Size of the filter kernel in the x-direction.
- filtery -- Size of the filter kernel in the y-direction.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- args.append("--filterx={}".format(filterx))
- args.append("--filtery={}".format(filtery))
- return self.run_tool('maximum_filter', args, callback) # returns 1 if error
-
- def mean_filter(self, i, output, filterx=3, filtery=3, callback=None):
- """Performs a mean filter (low-pass filter) on an input image.
-
- Keyword arguments:
-
- i -- Input raster file.
- output -- Output raster file.
- filterx -- Size of the filter kernel in the x-direction.
- filtery -- Size of the filter kernel in the y-direction.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- args.append("--filterx={}".format(filterx))
- args.append("--filtery={}".format(filtery))
- return self.run_tool('mean_filter', args, callback) # returns 1 if error
-
- def median_filter(self, i, output, filterx=11, filtery=11, sig_digits=2, callback=None):
- """Performs a median filter on an input image.
-
- Keyword arguments:
-
- i -- Input raster file.
- output -- Output raster file.
- filterx -- Size of the filter kernel in the x-direction.
- filtery -- Size of the filter kernel in the y-direction.
- sig_digits -- Number of significant digits.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- args.append("--filterx={}".format(filterx))
- args.append("--filtery={}".format(filtery))
- args.append("--sig_digits={}".format(sig_digits))
- return self.run_tool('median_filter', args, callback) # returns 1 if error
-
- def minimum_filter(self, i, output, filterx=11, filtery=11, callback=None):
- """Assigns each cell in the output grid the minimum value in a moving window centred on each grid cell in the input raster.
-
- Keyword arguments:
-
- i -- Input raster file.
- output -- Output raster file.
- filterx -- Size of the filter kernel in the x-direction.
- filtery -- Size of the filter kernel in the y-direction.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- args.append("--filterx={}".format(filterx))
- args.append("--filtery={}".format(filtery))
- return self.run_tool('minimum_filter', args, callback) # returns 1 if error
-
- def olympic_filter(self, i, output, filterx=11, filtery=11, callback=None):
- """Performs an olympic smoothing filter on an image.
-
- Keyword arguments:
-
- i -- Input raster file.
- output -- Output raster file.
- filterx -- Size of the filter kernel in the x-direction.
- filtery -- Size of the filter kernel in the y-direction.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- args.append("--filterx={}".format(filterx))
- args.append("--filtery={}".format(filtery))
- return self.run_tool('olympic_filter', args, callback) # returns 1 if error
-
- def percentile_filter(self, i, output, filterx=11, filtery=11, sig_digits=2, callback=None):
- """Performs a percentile filter on an input image.
-
- Keyword arguments:
-
- i -- Input raster file.
- output -- Output raster file.
- filterx -- Size of the filter kernel in the x-direction.
- filtery -- Size of the filter kernel in the y-direction.
- sig_digits -- Number of significant digits.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- args.append("--filterx={}".format(filterx))
- args.append("--filtery={}".format(filtery))
- args.append("--sig_digits={}".format(sig_digits))
- return self.run_tool('percentile_filter', args, callback) # returns 1 if error
-
- def prewitt_filter(self, i, output, clip=0.0, callback=None):
- """Performs a Prewitt edge-detection filter on an image.
-
- Keyword arguments:
-
- i -- Input raster file.
- output -- Output raster file.
- clip -- Optional amount to clip the distribution tails by, in percent.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- args.append("--clip={}".format(clip))
- return self.run_tool('prewitt_filter', args, callback) # returns 1 if error
-
- def range_filter(self, i, output, filterx=11, filtery=11, callback=None):
- """Assigns each cell in the output grid the range of values in a moving window centred on each grid cell in the input raster.
-
- Keyword arguments:
-
- i -- Input raster file.
- output -- Output raster file.
- filterx -- Size of the filter kernel in the x-direction.
- filtery -- Size of the filter kernel in the y-direction.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- args.append("--filterx={}".format(filterx))
- args.append("--filtery={}".format(filtery))
- return self.run_tool('range_filter', args, callback) # returns 1 if error
-
- def roberts_cross_filter(self, i, output, clip=0.0, callback=None):
- """Performs a Robert's cross edge-detection filter on an image.
-
- Keyword arguments:
-
- i -- Input raster file.
- output -- Output raster file.
- clip -- Optional amount to clip the distribution tails by, in percent.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- args.append("--clip={}".format(clip))
- return self.run_tool('roberts_cross_filter', args, callback) # returns 1 if error
-
- def scharr_filter(self, i, output, clip=0.0, callback=None):
- """Performs a Scharr edge-detection filter on an image.
-
- Keyword arguments:
-
- i -- Input raster file.
- output -- Output raster file.
- clip -- Optional amount to clip the distribution tails by, in percent.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- args.append("--clip={}".format(clip))
- return self.run_tool('scharr_filter', args, callback) # returns 1 if error
-
- def sobel_filter(self, i, output, variant="3x3", clip=0.0, callback=None):
- """Performs a Sobel edge-detection filter on an image.
-
- Keyword arguments:
-
- i -- Input raster file.
- output -- Output raster file.
- variant -- Optional variant value. Options include 3x3 and 5x5 (default is 3x3).
- clip -- Optional amount to clip the distribution tails by, in percent (default is 0.0).
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- args.append("--variant={}".format(variant))
- args.append("--clip={}".format(clip))
- return self.run_tool('sobel_filter', args, callback) # returns 1 if error
-
- def standard_deviation_filter(self, i, output, filterx=11, filtery=11, callback=None):
- """Assigns each cell in the output grid the standard deviation of values in a moving window centred on each grid cell in the input raster.
-
- Keyword arguments:
-
- i -- Input raster file.
- output -- Output raster file.
- filterx -- Size of the filter kernel in the x-direction.
- filtery -- Size of the filter kernel in the y-direction.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- args.append("--filterx={}".format(filterx))
- args.append("--filtery={}".format(filtery))
- return self.run_tool('standard_deviation_filter', args, callback) # returns 1 if error
-
- def total_filter(self, i, output, filterx=11, filtery=11, callback=None):
- """Performs a total filter on an input image.
-
- Keyword arguments:
-
- i -- Input raster file.
- output -- Output raster file.
- filterx -- Size of the filter kernel in the x-direction.
- filtery -- Size of the filter kernel in the y-direction.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- args.append("--filterx={}".format(filterx))
- args.append("--filtery={}".format(filtery))
- return self.run_tool('total_filter', args, callback) # returns 1 if error
-
- def unsharp_masking(self, i, output, sigma=0.75, amount=100.0, threshold=0.0, callback=None):
- """An image sharpening technique that enhances edges.
-
- Keyword arguments:
-
- i -- Input raster file.
- output -- Output raster file.
- sigma -- Standard deviation distance in pixels.
- amount -- A percentage and controls the magnitude of each overshoot.
- threshold -- Controls the minimal brightness change that will be sharpened.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- args.append("--sigma={}".format(sigma))
- args.append("--amount={}".format(amount))
- args.append("--threshold={}".format(threshold))
- return self.run_tool('unsharp_masking', args, callback) # returns 1 if error
-
- def user_defined_weights_filter(self, i, weights, output, center="center", normalize=False, callback=None):
- """Performs a user-defined weights filter on an image.
-
- Keyword arguments:
-
- i -- Input raster file.
- weights -- Input weights file.
- output -- Output raster file.
- center -- Kernel center cell; options include 'center', 'upper-left', 'upper-right', 'lower-left', 'lower-right'.
- normalize -- Normalize kernel weights? This can reduce edge effects and lessen the impact of data gaps (nodata) but is not suited when the kernel weights sum to zero.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--weights='{}'".format(weights))
- args.append("--output='{}'".format(output))
- args.append("--center={}".format(center))
- if normalize: args.append("--normalize")
- return self.run_tool('user_defined_weights_filter', args, callback) # returns 1 if error
-
- ############################################
- # Image Processing Tools/Image Enhancement #
- ############################################
-
- def balance_contrast_enhancement(self, i, output, band_mean=100.0, callback=None):
- """Performs a balance contrast enhancement on a colour-composite image of multispectral data.
-
- Keyword arguments:
-
- i -- Input colour composite image file.
- output -- Output raster file.
- band_mean -- Band mean value.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- args.append("--band_mean={}".format(band_mean))
- return self.run_tool('balance_contrast_enhancement', args, callback) # returns 1 if error
-
- def correct_vignetting(self, i, pp, output, focal_length=304.8, image_width=228.6, n=4.0, callback=None):
- """Corrects the darkening of images towards corners.
-
- Keyword arguments:
-
- i -- Input raster file.
- pp -- Input principal point file.
- output -- Output raster file.
- focal_length -- Camera focal length, in millimeters.
- image_width -- Distance between photograph edges, in millimeters.
- n -- The 'n' parameter.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--pp='{}'".format(pp))
- args.append("--output='{}'".format(output))
- args.append("--focal_length={}".format(focal_length))
- args.append("--image_width={}".format(image_width))
- args.append("-n={}".format(n))
- return self.run_tool('correct_vignetting', args, callback) # returns 1 if error
-
- def direct_decorrelation_stretch(self, i, output, k=0.5, clip=1.0, callback=None):
- """Performs a direct decorrelation stretch enhancement on a colour-composite image of multispectral data.
-
- Keyword arguments:
-
- i -- Input colour composite image file.
- output -- Output raster file.
- k -- Achromatic factor (k) ranges between 0 (no effect) and 1 (full saturation stretch), although typical values range from 0.3 to 0.7.
- clip -- Optional percent to clip the upper tail by during the stretch.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- args.append("-k={}".format(k))
- args.append("--clip={}".format(clip))
- return self.run_tool('direct_decorrelation_stretch', args, callback) # returns 1 if error
-
- def gamma_correction(self, i, output, gamma=0.5, callback=None):
- """Performs a gamma correction on an input images.
-
- Keyword arguments:
-
- i -- Input raster file.
- output -- Output raster file.
- gamma -- Gamma value.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- args.append("--gamma={}".format(gamma))
- return self.run_tool('gamma_correction', args, callback) # returns 1 if error
-
- def gaussian_contrast_stretch(self, i, output, num_tones=256, callback=None):
- """Performs a Gaussian contrast stretch on input images.
-
- Keyword arguments:
-
- i -- Input raster file.
- output -- Output raster file.
- num_tones -- Number of tones in the output image.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- args.append("--num_tones={}".format(num_tones))
- return self.run_tool('gaussian_contrast_stretch', args, callback) # returns 1 if error
-
- def histogram_equalization(self, i, output, num_tones=256, callback=None):
- """Performs a histogram equalization contrast enhancment on an image.
-
- Keyword arguments:
-
- i -- Input raster file.
- output -- Output raster file.
- num_tones -- Number of tones in the output image.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- args.append("--num_tones={}".format(num_tones))
- return self.run_tool('histogram_equalization', args, callback) # returns 1 if error
-
- def histogram_matching(self, i, histo_file, output, callback=None):
- """Alters the statistical distribution of a raster image matching it to a specified PDF.
-
- Keyword arguments:
-
- i -- Input raster file.
- histo_file -- Input reference probability distribution function (pdf) text file.
- output -- Output raster file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--histo_file='{}'".format(histo_file))
- args.append("--output='{}'".format(output))
- return self.run_tool('histogram_matching', args, callback) # returns 1 if error
-
- def histogram_matching_two_images(self, input1, input2, output, callback=None):
- """This tool alters the cumulative distribution function of a raster image to that of another image.
-
- Keyword arguments:
-
- input1 -- Input raster file to modify.
- input2 -- Input reference raster file.
- output -- Output raster file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input1='{}'".format(input1))
- args.append("--input2='{}'".format(input2))
- args.append("--output='{}'".format(output))
- return self.run_tool('histogram_matching_two_images', args, callback) # returns 1 if error
-
- def min_max_contrast_stretch(self, i, output, min_val, max_val, num_tones=256, callback=None):
- """Performs a min-max contrast stretch on an input greytone image.
-
- Keyword arguments:
-
- i -- Input raster file.
- output -- Output raster file.
- min_val -- Lower tail clip value.
- max_val -- Upper tail clip value.
- num_tones -- Number of tones in the output image.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- args.append("--min_val='{}'".format(min_val))
- args.append("--max_val='{}'".format(max_val))
- args.append("--num_tones={}".format(num_tones))
- return self.run_tool('min_max_contrast_stretch', args, callback) # returns 1 if error
-
- def panchromatic_sharpening(self, pan, output, red=None, green=None, blue=None, composite=None, method="brovey", callback=None):
- """Increases the spatial resolution of image data by combining multispectral bands with panchromatic data.
-
- Keyword arguments:
-
- red -- Input red band image file. Optionally specified if colour-composite not specified.
- green -- Input green band image file. Optionally specified if colour-composite not specified.
- blue -- Input blue band image file. Optionally specified if colour-composite not specified.
- composite -- Input colour-composite image file. Only used if individual bands are not specified.
- pan -- Input panchromatic band file.
- output -- Output colour composite file.
- method -- Options include 'brovey' (default) and 'ihs'.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- if red is not None: args.append("--red='{}'".format(red))
- if green is not None: args.append("--green='{}'".format(green))
- if blue is not None: args.append("--blue='{}'".format(blue))
- if composite is not None: args.append("--composite='{}'".format(composite))
- args.append("--pan='{}'".format(pan))
- args.append("--output='{}'".format(output))
- args.append("--method={}".format(method))
- return self.run_tool('panchromatic_sharpening', args, callback) # returns 1 if error
-
- def percentage_contrast_stretch(self, i, output, clip=1.0, tail="both", num_tones=256, callback=None):
- """Performs a percentage linear contrast stretch on input images.
-
- Keyword arguments:
-
- i -- Input raster file.
- output -- Output raster file.
- clip -- Optional amount to clip the distribution tails by, in percent.
- tail -- Specified which tails to clip; options include 'upper', 'lower', and 'both' (default is 'both').
- num_tones -- Number of tones in the output image.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- args.append("--clip={}".format(clip))
- args.append("--tail={}".format(tail))
- args.append("--num_tones={}".format(num_tones))
- return self.run_tool('percentage_contrast_stretch', args, callback) # returns 1 if error
-
- def sigmoidal_contrast_stretch(self, i, output, cutoff=0.0, gain=1.0, num_tones=256, callback=None):
- """Performs a sigmoidal contrast stretch on input images.
-
- Keyword arguments:
-
- i -- Input raster file.
- output -- Output raster file.
- cutoff -- Cutoff value between 0.0 and 0.95.
- gain -- Gain value.
- num_tones -- Number of tones in the output image.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- args.append("--cutoff={}".format(cutoff))
- args.append("--gain={}".format(gain))
- args.append("--num_tones={}".format(num_tones))
- return self.run_tool('sigmoidal_contrast_stretch', args, callback) # returns 1 if error
-
- def standard_deviation_contrast_stretch(self, i, output, stdev=2.0, num_tones=256, callback=None):
- """Performs a standard-deviation contrast stretch on input images.
-
- Keyword arguments:
-
- i -- Input raster file.
- output -- Output raster file.
- stdev -- Standard deviation clip value.
- num_tones -- Number of tones in the output image.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- args.append("--stdev={}".format(stdev))
- args.append("--num_tones={}".format(num_tones))
- return self.run_tool('standard_deviation_contrast_stretch', args, callback) # returns 1 if error
-
- ###############
- # LiDAR Tools #
- ###############
-
- def classify_overlap_points(self, i, output, resolution=2.0, filter=False, callback=None):
- """Classifies or filters LAS points in regions of overlapping flight lines.
-
- Keyword arguments:
-
- i -- Input LiDAR file.
- output -- Output LiDAR file.
- resolution -- The size of the square area used to evaluate nearby points in the LiDAR data.
- filter -- Filter out points from overlapping flightlines? If false, overlaps will simply be classified.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- args.append("--resolution={}".format(resolution))
- if filter: args.append("--filter")
- return self.run_tool('classify_overlap_points', args, callback) # returns 1 if error
-
- def clip_lidar_to_polygon(self, i, polygons, output, callback=None):
- """Clips a LiDAR point cloud to a vector polygon or polygons.
-
- Keyword arguments:
-
- i -- Input LiDAR file.
- polygons -- Input vector polygons file.
- output -- Output LiDAR file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--polygons='{}'".format(polygons))
- args.append("--output='{}'".format(output))
- return self.run_tool('clip_lidar_to_polygon', args, callback) # returns 1 if error
-
- def erase_polygon_from_lidar(self, i, polygons, output, callback=None):
- """Erases (cuts out) a vector polygon or polygons from a LiDAR point cloud.
-
- Keyword arguments:
-
- i -- Input LiDAR file.
- polygons -- Input vector polygons file.
- output -- Output LiDAR file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--polygons='{}'".format(polygons))
- args.append("--output='{}'".format(output))
- return self.run_tool('erase_polygon_from_lidar', args, callback) # returns 1 if error
-
- def filter_lidar_scan_angles(self, i, output, threshold, callback=None):
- """Removes points in a LAS file with scan angles greater than a threshold.
-
- Keyword arguments:
-
- i -- Input LiDAR file.
- output -- Output LiDAR file.
- threshold -- Scan angle threshold.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- args.append("--threshold='{}'".format(threshold))
- return self.run_tool('filter_lidar_scan_angles', args, callback) # returns 1 if error
-
- def find_flightline_edge_points(self, i, output, callback=None):
- """Identifies points along a flightline's edge in a LAS file.
-
- Keyword arguments:
-
- i -- Input LiDAR file.
- output -- Output file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- return self.run_tool('find_flightline_edge_points', args, callback) # returns 1 if error
-
- def flightline_overlap(self, i=None, output=None, resolution=1.0, callback=None):
- """Reads a LiDAR (LAS) point file and outputs a raster containing the number of overlapping flight lines in each grid cell.
-
- Keyword arguments:
-
- i -- Input LiDAR file.
- output -- Output file.
- resolution -- Output raster's grid resolution.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- if i is not None: args.append("--input='{}'".format(i))
- if output is not None: args.append("--output='{}'".format(output))
- args.append("--resolution={}".format(resolution))
- return self.run_tool('flightline_overlap', args, callback) # returns 1 if error
-
- def las_to_ascii(self, inputs, callback=None):
- """Converts one or more LAS files into ASCII text files.
-
- Keyword arguments:
-
- inputs -- Input LiDAR files.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--inputs='{}'".format(inputs))
- return self.run_tool('las_to_ascii', args, callback) # returns 1 if error
-
- def las_to_multipoint_shapefile(self, i=None, callback=None):
- """Converts one or more LAS files into MultipointZ vector Shapefiles. When the input parameter is not specified, the tool grids all LAS files contained within the working directory.
-
- Keyword arguments:
-
- i -- Input LiDAR file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- if i is not None: args.append("--input='{}'".format(i))
- return self.run_tool('las_to_multipoint_shapefile', args, callback) # returns 1 if error
-
- def las_to_shapefile(self, i=None, callback=None):
- """Converts one or more LAS files into a vector Shapefile of POINT ShapeType.
-
- Keyword arguments:
-
- i -- Input LiDAR file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- if i is not None: args.append("--input='{}'".format(i))
- return self.run_tool('las_to_shapefile', args, callback) # returns 1 if error
-
- def lidar_block_maximum(self, i=None, output=None, resolution=1.0, callback=None):
- """Creates a block-maximum raster from an input LAS file. When the input/output parameters are not specified, the tool grids all LAS files contained within the working directory.
-
- Keyword arguments:
-
- i -- Input LiDAR file.
- output -- Output file.
- resolution -- Output raster's grid resolution.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- if i is not None: args.append("--input='{}'".format(i))
- if output is not None: args.append("--output='{}'".format(output))
- args.append("--resolution={}".format(resolution))
- return self.run_tool('lidar_block_maximum', args, callback) # returns 1 if error
-
- def lidar_block_minimum(self, i=None, output=None, resolution=1.0, callback=None):
- """Creates a block-minimum raster from an input LAS file. When the input/output parameters are not specified, the tool grids all LAS files contained within the working directory.
-
- Keyword arguments:
-
- i -- Input LiDAR file.
- output -- Output file.
- resolution -- Output raster's grid resolution.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- if i is not None: args.append("--input='{}'".format(i))
- if output is not None: args.append("--output='{}'".format(output))
- args.append("--resolution={}".format(resolution))
- return self.run_tool('lidar_block_minimum', args, callback) # returns 1 if error
-
- def lidar_classify_subset(self, base, subset, output, subset_class, nonsubset_class=None, callback=None):
- """Classifies the values in one LiDAR point cloud that correpond with points in a subset cloud.
-
- Keyword arguments:
-
- base -- Input base LiDAR file.
- subset -- Input subset LiDAR file.
- output -- Output LiDAR file.
- subset_class -- Subset point class value (must be 0-18; see LAS specifications).
- nonsubset_class -- Non-subset point class value (must be 0-18; see LAS specifications).
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--base='{}'".format(base))
- args.append("--subset='{}'".format(subset))
- args.append("--output='{}'".format(output))
- args.append("--subset_class='{}'".format(subset_class))
- if nonsubset_class is not None: args.append("--nonsubset_class='{}'".format(nonsubset_class))
- return self.run_tool('lidar_classify_subset', args, callback) # returns 1 if error
-
- def lidar_colourize(self, in_lidar, in_image, output, callback=None):
- """Adds the red-green-blue colour fields of a LiDAR (LAS) file based on an input image.
-
- Keyword arguments:
-
- in_lidar -- Input LiDAR file.
- in_image -- Input colour image file.
- output -- Output LiDAR file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--in_lidar='{}'".format(in_lidar))
- args.append("--in_image='{}'".format(in_image))
- args.append("--output='{}'".format(output))
- return self.run_tool('lidar_colourize', args, callback) # returns 1 if error
-
- def lidar_construct_vector_tin(self, i=None, output=None, returns="all", exclude_cls=None, minz=None, maxz=None, callback=None):
- """Creates a vector triangular irregular network (TIN) fitted to LiDAR points.
-
- Keyword arguments:
-
- i -- Input LiDAR file (including extension).
- output -- Output raster file (including extension).
- returns -- Point return types to include; options are 'all' (default), 'last', 'first'.
- exclude_cls -- Optional exclude classes from interpolation; Valid class values range from 0 to 18, based on LAS specifications. Example, --exclude_cls='3,4,5,6,7,18'.
- minz -- Optional minimum elevation for inclusion in interpolation.
- maxz -- Optional maximum elevation for inclusion in interpolation.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- if i is not None: args.append("--input='{}'".format(i))
- if output is not None: args.append("--output='{}'".format(output))
- args.append("--returns={}".format(returns))
- if exclude_cls is not None: args.append("--exclude_cls='{}'".format(exclude_cls))
- if minz is not None: args.append("--minz='{}'".format(minz))
- if maxz is not None: args.append("--maxz='{}'".format(maxz))
- return self.run_tool('lidar_construct_vector_tin', args, callback) # returns 1 if error
-
- def lidar_elevation_slice(self, i, output, minz=None, maxz=None, cls=False, inclassval=2, outclassval=1, callback=None):
- """Outputs all of the points within a LiDAR (LAS) point file that lie between a specified elevation range.
-
- Keyword arguments:
-
- i -- Input LiDAR file.
- output -- Output LiDAR file.
- minz -- Minimum elevation value (optional).
- maxz -- Maximum elevation value (optional).
- cls -- Optional boolean flag indicating whether points outside the range should be retained in output but reclassified.
- inclassval -- Optional parameter specifying the class value assigned to points within the slice.
- outclassval -- Optional parameter specifying the class value assigned to points within the slice.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- if minz is not None: args.append("--minz='{}'".format(minz))
- if maxz is not None: args.append("--maxz='{}'".format(maxz))
- if cls: args.append("--class")
- args.append("--inclassval={}".format(inclassval))
- args.append("--outclassval={}".format(outclassval))
- return self.run_tool('lidar_elevation_slice', args, callback) # returns 1 if error
-
- def lidar_ground_point_filter(self, i, output, radius=2.0, min_neighbours=0, slope_threshold=45.0, height_threshold=1.0, classify=True, slope_norm=True, callback=None):
- """Identifies ground points within LiDAR dataset using a slope-based method.
-
- Keyword arguments:
-
- i -- Input LiDAR file.
- output -- Output LiDAR file.
- radius -- Search Radius.
- min_neighbours -- The minimum number of neighbouring points within search areas. If fewer points than this threshold are idenfied during the fixed-radius search, a subsequent kNN search is performed to identify the k number of neighbours.
- slope_threshold -- Maximum inter-point slope to be considered an off-terrain point.
- height_threshold -- Inter-point height difference to be considered an off-terrain point.
- classify -- Classify points as ground (2) or off-ground (1).
- slope_norm -- Perform initial ground slope normalization?.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- args.append("--radius={}".format(radius))
- args.append("--min_neighbours={}".format(min_neighbours))
- args.append("--slope_threshold={}".format(slope_threshold))
- args.append("--height_threshold={}".format(height_threshold))
- if classify: args.append("--classify")
- if slope_norm: args.append("--slope_norm")
- return self.run_tool('lidar_ground_point_filter', args, callback) # returns 1 if error
-
- def lidar_hex_binning(self, i, output, width, orientation="horizontal", callback=None):
- """Hex-bins a set of LiDAR points.
-
- Keyword arguments:
-
- i -- Input base file.
- output -- Output vector polygon file.
- width -- The grid cell width.
- orientation -- Grid Orientation, 'horizontal' or 'vertical'.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- args.append("--width='{}'".format(width))
- args.append("--orientation={}".format(orientation))
- return self.run_tool('lidar_hex_binning', args, callback) # returns 1 if error
-
- def lidar_hillshade(self, i, output, azimuth=315.0, altitude=30.0, radius=1.0, callback=None):
- """Calculates a hillshade value for points within a LAS file and stores these data in the RGB field.
-
- Keyword arguments:
-
- i -- Input LiDAR file.
- output -- Output file.
- azimuth -- Illumination source azimuth in degrees.
- altitude -- Illumination source altitude in degrees.
- radius -- Search Radius.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- args.append("--azimuth={}".format(azimuth))
- args.append("--altitude={}".format(altitude))
- args.append("--radius={}".format(radius))
- return self.run_tool('lidar_hillshade', args, callback) # returns 1 if error
-
- def lidar_histogram(self, i, output, parameter="elevation", clip=1.0, callback=None):
- """Creates a histogram of LiDAR data.
-
- Keyword arguments:
-
- i -- Input LiDAR file.
- output -- Output HTML file (default name will be based on input file if unspecified).
- parameter -- Parameter; options are 'elevation' (default), 'intensity', 'scan angle', 'class'.
- clip -- Amount to clip distribution tails (in percent).
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- args.append("--parameter={}".format(parameter))
- args.append("--clip={}".format(clip))
- return self.run_tool('lidar_histogram', args, callback) # returns 1 if error
-
- def lidar_idw_interpolation(self, i=None, output=None, parameter="elevation", returns="all", resolution=1.0, weight=1.0, radius=2.5, exclude_cls=None, minz=None, maxz=None, callback=None):
- """Interpolates LAS files using an inverse-distance weighted (IDW) scheme. When the input/output parameters are not specified, the tool interpolates all LAS files contained within the working directory.
-
- Keyword arguments:
-
- i -- Input LiDAR file (including extension).
- output -- Output raster file (including extension).
- parameter -- Interpolation parameter; options are 'elevation' (default), 'intensity', 'class', 'scan angle', 'user data'.
- returns -- Point return types to include; options are 'all' (default), 'last', 'first'.
- resolution -- Output raster's grid resolution.
- weight -- IDW weight value.
- radius -- Search Radius.
- exclude_cls -- Optional exclude classes from interpolation; Valid class values range from 0 to 18, based on LAS specifications. Example, --exclude_cls='3,4,5,6,7,18'.
- minz -- Optional minimum elevation for inclusion in interpolation.
- maxz -- Optional maximum elevation for inclusion in interpolation.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- if i is not None: args.append("--input='{}'".format(i))
- if output is not None: args.append("--output='{}'".format(output))
- args.append("--parameter={}".format(parameter))
- args.append("--returns={}".format(returns))
- args.append("--resolution={}".format(resolution))
- args.append("--weight={}".format(weight))
- args.append("--radius={}".format(radius))
- if exclude_cls is not None: args.append("--exclude_cls='{}'".format(exclude_cls))
- if minz is not None: args.append("--minz='{}'".format(minz))
- if maxz is not None: args.append("--maxz='{}'".format(maxz))
- return self.run_tool('lidar_idw_interpolation', args, callback) # returns 1 if error
-
- def lidar_info(self, i, output=None, vlr=False, geokeys=False, callback=None):
- """Prints information about a LiDAR (LAS) dataset, including header, point return frequency, and classification data and information about the variable length records (VLRs) and geokeys.
-
- Keyword arguments:
-
- i -- Input LiDAR file.
- output -- Output HTML file for summary report.
- vlr -- Flag indicating whether or not to print the variable length records (VLRs).
- geokeys -- Flag indicating whether or not to print the geokeys.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- if output is not None: args.append("--output='{}'".format(output))
- if vlr: args.append("--vlr")
- if geokeys: args.append("--geokeys")
- return self.run_tool('lidar_info', args, callback) # returns 1 if error
-
- def lidar_join(self, inputs, output, callback=None):
- """Joins multiple LiDAR (LAS) files into a single LAS file.
-
- Keyword arguments:
-
- inputs -- Input LiDAR files.
- output -- Output LiDAR file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--inputs='{}'".format(inputs))
- args.append("--output='{}'".format(output))
- return self.run_tool('lidar_join', args, callback) # returns 1 if error
-
- def lidar_kappa_index(self, input1, input2, output, class_accuracy, resolution=1.0, callback=None):
- """Performs a kappa index of agreement (KIA) analysis on the classifications of two LAS files.
-
- Keyword arguments:
-
- input1 -- Input LiDAR classification file.
- input2 -- Input LiDAR reference file.
- output -- Output HTML file.
- class_accuracy -- Output classification accuracy raster file.
- resolution -- Output raster's grid resolution.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input1='{}'".format(input1))
- args.append("--input2='{}'".format(input2))
- args.append("--output='{}'".format(output))
- args.append("--class_accuracy='{}'".format(class_accuracy))
- args.append("--resolution={}".format(resolution))
- return self.run_tool('lidar_kappa_index', args, callback) # returns 1 if error
-
- def lidar_nearest_neighbour_gridding(self, i=None, output=None, parameter="elevation", returns="all", resolution=1.0, radius=2.5, exclude_cls=None, minz=None, maxz=None, callback=None):
- """Grids LAS files using nearest-neighbour scheme. When the input/output parameters are not specified, the tool grids all LAS files contained within the working directory.
-
- Keyword arguments:
-
- i -- Input LiDAR file (including extension).
- output -- Output raster file (including extension).
- parameter -- Interpolation parameter; options are 'elevation' (default), 'intensity', 'class', 'scan angle', 'user data'.
- returns -- Point return types to include; options are 'all' (default), 'last', 'first'.
- resolution -- Output raster's grid resolution.
- radius -- Search Radius.
- exclude_cls -- Optional exclude classes from interpolation; Valid class values range from 0 to 18, based on LAS specifications. Example, --exclude_cls='3,4,5,6,7,18'.
- minz -- Optional minimum elevation for inclusion in interpolation.
- maxz -- Optional maximum elevation for inclusion in interpolation.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- if i is not None: args.append("--input='{}'".format(i))
- if output is not None: args.append("--output='{}'".format(output))
- args.append("--parameter={}".format(parameter))
- args.append("--returns={}".format(returns))
- args.append("--resolution={}".format(resolution))
- args.append("--radius={}".format(radius))
- if exclude_cls is not None: args.append("--exclude_cls='{}'".format(exclude_cls))
- if minz is not None: args.append("--minz='{}'".format(minz))
- if maxz is not None: args.append("--maxz='{}'".format(maxz))
- return self.run_tool('lidar_nearest_neighbour_gridding', args, callback) # returns 1 if error
-
- def lidar_point_density(self, i=None, output=None, returns="all", resolution=1.0, radius=2.5, exclude_cls=None, minz=None, maxz=None, callback=None):
- """Calculates the spatial pattern of point density for a LiDAR data set. When the input/output parameters are not specified, the tool grids all LAS files contained within the working directory.
-
- Keyword arguments:
-
- i -- Input LiDAR file (including extension).
- output -- Output raster file (including extension).
- returns -- Point return types to include; options are 'all' (default), 'last', 'first'.
- resolution -- Output raster's grid resolution.
- radius -- Search Radius.
- exclude_cls -- Optional exclude classes from interpolation; Valid class values range from 0 to 18, based on LAS specifications. Example, --exclude_cls='3,4,5,6,7,18'.
- minz -- Optional minimum elevation for inclusion in interpolation.
- maxz -- Optional maximum elevation for inclusion in interpolation.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- if i is not None: args.append("--input='{}'".format(i))
- if output is not None: args.append("--output='{}'".format(output))
- args.append("--returns={}".format(returns))
- args.append("--resolution={}".format(resolution))
- args.append("--radius={}".format(radius))
- if exclude_cls is not None: args.append("--exclude_cls='{}'".format(exclude_cls))
- if minz is not None: args.append("--minz='{}'".format(minz))
- if maxz is not None: args.append("--maxz='{}'".format(maxz))
- return self.run_tool('lidar_point_density', args, callback) # returns 1 if error
-
- def lidar_point_stats(self, i=None, resolution=1.0, num_points=True, num_pulses=False, z_range=False, intensity_range=False, predom_class=False, callback=None):
- """Creates several rasters summarizing the distribution of LAS point data. When the input/output parameters are not specified, the tool works on all LAS files contained within the working directory.
-
- Keyword arguments:
-
- i -- Input LiDAR file.
- resolution -- Output raster's grid resolution.
- num_points -- Flag indicating whether or not to output the number of points raster.
- num_pulses -- Flag indicating whether or not to output the number of pulses raster.
- z_range -- Flag indicating whether or not to output the elevation range raster.
- intensity_range -- Flag indicating whether or not to output the intensity range raster.
- predom_class -- Flag indicating whether or not to output the predominant classification raster.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- if i is not None: args.append("--input='{}'".format(i))
- args.append("--resolution={}".format(resolution))
- if num_points: args.append("--num_points")
- if num_pulses: args.append("--num_pulses")
- if z_range: args.append("--z_range")
- if intensity_range: args.append("--intensity_range")
- if predom_class: args.append("--predom_class")
- return self.run_tool('lidar_point_stats', args, callback) # returns 1 if error
-
- def lidar_remove_duplicates(self, i, output, include_z=False, callback=None):
- """Removes duplicate points from a LiDAR data set.
-
- Keyword arguments:
-
- i -- Input LiDAR file.
- output -- Output LiDAR file.
- include_z -- Include z-values in point comparison?.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- if include_z: args.append("--include_z")
- return self.run_tool('lidar_remove_duplicates', args, callback) # returns 1 if error
-
- def lidar_remove_outliers(self, i, output, radius=2.0, elev_diff=50.0, callback=None):
- """Removes outliers (high and low points) in a LiDAR point cloud.
-
- Keyword arguments:
-
- i -- Input LiDAR file.
- output -- Output LiDAR file.
- radius -- Search Radius.
- elev_diff -- Max. elevation difference.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- args.append("--radius={}".format(radius))
- args.append("--elev_diff={}".format(elev_diff))
- return self.run_tool('lidar_remove_outliers', args, callback) # returns 1 if error
-
- def lidar_segmentation(self, i, output, radius=5.0, norm_diff=10.0, maxzdiff=1.0, callback=None):
- """Segments a LiDAR point cloud based on normal vectors.
-
- Keyword arguments:
-
- i -- Input LiDAR file.
- output -- Output file.
- radius -- Search Radius.
- norm_diff -- Maximum difference in normal vectors, in degrees.
- maxzdiff -- Maximum difference in elevation (z units) between neighbouring points of the same segment.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- args.append("--radius={}".format(radius))
- args.append("--norm_diff={}".format(norm_diff))
- args.append("--maxzdiff={}".format(maxzdiff))
- return self.run_tool('lidar_segmentation', args, callback) # returns 1 if error
-
- def lidar_segmentation_based_filter(self, i, output, radius=5.0, norm_diff=2.0, maxzdiff=1.0, classify=False, callback=None):
- """Identifies ground points within LiDAR point clouds using a segmentation based approach.
-
- Keyword arguments:
-
- i -- Input LiDAR file.
- output -- Output file.
- radius -- Search Radius.
- norm_diff -- Maximum difference in normal vectors, in degrees.
- maxzdiff -- Maximum difference in elevation (z units) between neighbouring points of the same segment.
- classify -- Classify points as ground (2) or off-ground (1).
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- args.append("--radius={}".format(radius))
- args.append("--norm_diff={}".format(norm_diff))
- args.append("--maxzdiff={}".format(maxzdiff))
- if classify: args.append("--classify")
- return self.run_tool('lidar_segmentation_based_filter', args, callback) # returns 1 if error
-
- def lidar_thin(self, i, output, resolution=2.0, method="lowest", save_filtered=False, callback=None):
- """Thins a LiDAR point cloud, reducing point density.
-
- Keyword arguments:
-
- i -- Input LiDAR file.
- output -- Output LiDAR file.
- resolution -- The size of the square area used to evaluate nearby points in the LiDAR data.
- method -- Point selection method; options are 'first', 'last', 'lowest' (default), 'highest', 'nearest'.
- save_filtered -- Save filtered points to seperate file?.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- args.append("--resolution={}".format(resolution))
- args.append("--method={}".format(method))
- if save_filtered: args.append("--save_filtered")
- return self.run_tool('lidar_thin', args, callback) # returns 1 if error
-
- def lidar_thin_high_density(self, i, output, density, resolution=1.0, save_filtered=False, callback=None):
- """Thins points from high density areas within a LiDAR point cloud.
-
- Keyword arguments:
-
- i -- Input LiDAR file.
- output -- Output LiDAR file.
- resolution -- Output raster's grid resolution.
- density -- Max. point density (points / m^3).
- save_filtered -- Save filtered points to seperate file?.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- args.append("--resolution={}".format(resolution))
- args.append("--density='{}'".format(density))
- if save_filtered: args.append("--save_filtered")
- return self.run_tool('lidar_thin_high_density', args, callback) # returns 1 if error
-
- def lidar_tile(self, i, width=1000.0, height=1000.0, origin_x=0.0, origin_y=0.0, min_points=2, callback=None):
- """Tiles a LiDAR LAS file into multiple LAS files.
-
- Keyword arguments:
-
- i -- Input LiDAR file.
- width -- Width of tiles in the X dimension; default 1000.0.
- height -- Height of tiles in the Y dimension.
- origin_x -- Origin point X coordinate for tile grid.
- origin_y -- Origin point Y coordinate for tile grid.
- min_points -- Minimum number of points contained in a tile for it to be saved.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--width={}".format(width))
- args.append("--height={}".format(height))
- args.append("--origin_x={}".format(origin_x))
- args.append("--origin_y={}".format(origin_y))
- args.append("--min_points={}".format(min_points))
- return self.run_tool('lidar_tile', args, callback) # returns 1 if error
-
- def lidar_tile_footprint(self, output, i=None, callback=None):
- """Creates a vector polygon of the convex hull of a LiDAR point cloud. When the input/output parameters are not specified, the tool works with all LAS files contained within the working directory.
-
- Keyword arguments:
-
- i -- Input LiDAR file.
- output -- Output vector polygon file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- if i is not None: args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- return self.run_tool('lidar_tile_footprint', args, callback) # returns 1 if error
-
- def lidar_tin_gridding(self, i=None, output=None, parameter="elevation", returns="all", resolution=1.0, exclude_cls=None, minz=None, maxz=None, max_triangle_edge_length=None, callback=None):
- """Creates a raster grid based on a Delaunay triangular irregular network (TIN) fitted to LiDAR points.
-
- Keyword arguments:
-
- i -- Input LiDAR file (including extension).
- output -- Output raster file (including extension).
- parameter -- Interpolation parameter; options are 'elevation' (default), 'intensity', 'class', 'scan angle', 'user data'.
- returns -- Point return types to include; options are 'all' (default), 'last', 'first'.
- resolution -- Output raster's grid resolution.
- exclude_cls -- Optional exclude classes from interpolation; Valid class values range from 0 to 18, based on LAS specifications. Example, --exclude_cls='3,4,5,6,7,18'.
- minz -- Optional minimum elevation for inclusion in interpolation.
- maxz -- Optional maximum elevation for inclusion in interpolation.
- max_triangle_edge_length -- Optional maximum triangle edge length; triangles larger than this size will not be gridded.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- if i is not None: args.append("--input='{}'".format(i))
- if output is not None: args.append("--output='{}'".format(output))
- args.append("--parameter={}".format(parameter))
- args.append("--returns={}".format(returns))
- args.append("--resolution={}".format(resolution))
- if exclude_cls is not None: args.append("--exclude_cls='{}'".format(exclude_cls))
- if minz is not None: args.append("--minz='{}'".format(minz))
- if maxz is not None: args.append("--maxz='{}'".format(maxz))
- if max_triangle_edge_length is not None: args.append("--max_triangle_edge_length='{}'".format(max_triangle_edge_length))
- return self.run_tool('lidar_tin_gridding', args, callback) # returns 1 if error
-
- def lidar_tophat_transform(self, i, output, radius=1.0, callback=None):
- """Performs a white top-hat transform on a Lidar dataset; as an estimate of height above ground, this is useful for modelling the vegetation canopy.
-
- Keyword arguments:
-
- i -- Input LiDAR file.
- output -- Output LiDAR file.
- radius -- Search Radius.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- args.append("--radius={}".format(radius))
- return self.run_tool('lidar_tophat_transform', args, callback) # returns 1 if error
-
- def normal_vectors(self, i, output, radius=1.0, callback=None):
- """Calculates normal vectors for points within a LAS file and stores these data (XYZ vector components) in the RGB field.
-
- Keyword arguments:
-
- i -- Input LiDAR file.
- output -- Output LiDAR file.
- radius -- Search Radius.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- args.append("--radius={}".format(radius))
- return self.run_tool('normal_vectors', args, callback) # returns 1 if error
-
- def select_tiles_by_polygon(self, indir, outdir, polygons, callback=None):
- """Copies LiDAR tiles overlapping with a polygon into an output directory.
-
- Keyword arguments:
-
- indir -- Input LAS file source directory.
- outdir -- Output directory into which LAS files within the polygon are copied.
- polygons -- Input vector polygons file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--indir='{}'".format(indir))
- args.append("--outdir='{}'".format(outdir))
- args.append("--polygons='{}'".format(polygons))
- return self.run_tool('select_tiles_by_polygon', args, callback) # returns 1 if error
-
- ########################
- # Math and Stats Tools #
- ########################
-
- def And(self, input1, input2, output, callback=None):
- """Performs a logical AND operator on two Boolean raster images.
-
- Keyword arguments:
-
- input1 -- Input raster file.
- input2 -- Input raster file.
- output -- Output raster file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input1='{}'".format(input1))
- args.append("--input2='{}'".format(input2))
- args.append("--output='{}'".format(output))
- return self.run_tool('and', args, callback) # returns 1 if error
-
- def Not(self, input1, input2, output, callback=None):
- """Performs a logical NOT operator on two Boolean raster images.
-
- Keyword arguments:
-
- input1 -- Input raster file.
- input2 -- Input raster file.
- output -- Output raster file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input1='{}'".format(input1))
- args.append("--input2='{}'".format(input2))
- args.append("--output='{}'".format(output))
- return self.run_tool('not', args, callback) # returns 1 if error
-
- def Or(self, input1, input2, output, callback=None):
- """Performs a logical OR operator on two Boolean raster images.
-
- Keyword arguments:
-
- input1 -- Input raster file.
- input2 -- Input raster file.
- output -- Output raster file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input1='{}'".format(input1))
- args.append("--input2='{}'".format(input2))
- args.append("--output='{}'".format(output))
- return self.run_tool('or', args, callback) # returns 1 if error
-
- def absolute_value(self, i, output, callback=None):
- """Calculates the absolute value of every cell in a raster.
-
- Keyword arguments:
-
- i -- Input raster file.
- output -- Output raster file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- return self.run_tool('absolute_value', args, callback) # returns 1 if error
-
- def add(self, input1, input2, output, callback=None):
- """Performs an addition operation on two rasters or a raster and a constant value.
-
- Keyword arguments:
-
- input1 -- Input raster file or constant value.
- input2 -- Input raster file or constant value.
- output -- Output raster file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input1='{}'".format(input1))
- args.append("--input2='{}'".format(input2))
- args.append("--output='{}'".format(output))
- return self.run_tool('add', args, callback) # returns 1 if error
-
- def anova(self, i, features, output, callback=None):
- """Performs an analysis of variance (ANOVA) test on a raster dataset.
-
- Keyword arguments:
-
- i -- Input raster file.
- features -- Feature definition (or class) raster.
- output -- Output HTML file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--features='{}'".format(features))
- args.append("--output='{}'".format(output))
- return self.run_tool('anova', args, callback) # returns 1 if error
-
- def arc_cos(self, i, output, callback=None):
- """Returns the inverse cosine (arccos) of each values in a raster.
-
- Keyword arguments:
-
- i -- Input raster file.
- output -- Output raster file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- return self.run_tool('arc_cos', args, callback) # returns 1 if error
-
- def arc_sin(self, i, output, callback=None):
- """Returns the inverse sine (arcsin) of each values in a raster.
-
- Keyword arguments:
-
- i -- Input raster file.
- output -- Output raster file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- return self.run_tool('arc_sin', args, callback) # returns 1 if error
-
- def arc_tan(self, i, output, callback=None):
- """Returns the inverse tangent (arctan) of each values in a raster.
-
- Keyword arguments:
-
- i -- Input raster file.
- output -- Output raster file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- return self.run_tool('arc_tan', args, callback) # returns 1 if error
-
- def atan2(self, input_y, input_x, output, callback=None):
- """Returns the 2-argument inverse tangent (atan2).
-
- Keyword arguments:
-
- input_y -- Input y raster file or constant value (rise).
- input_x -- Input x raster file or constant value (run).
- output -- Output raster file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input_y='{}'".format(input_y))
- args.append("--input_x='{}'".format(input_x))
- args.append("--output='{}'".format(output))
- return self.run_tool('atan2', args, callback) # returns 1 if error
-
- def attribute_correlation(self, i, output=None, callback=None):
- """Performs a correlation analysis on attribute fields from a vector database.
-
- Keyword arguments:
-
- i -- Input raster file.
- output -- Output HTML file (default name will be based on input file if unspecified).
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- if output is not None: args.append("--output='{}'".format(output))
- return self.run_tool('attribute_correlation', args, callback) # returns 1 if error
-
- def attribute_histogram(self, i, field, output, callback=None):
- """Creates a histogram for the field values of a vector's attribute table.
-
- Keyword arguments:
-
- i -- Input raster file.
- field -- Input field name in attribute table.
- output -- Output HTML file (default name will be based on input file if unspecified).
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--field='{}'".format(field))
- args.append("--output='{}'".format(output))
- return self.run_tool('attribute_histogram', args, callback) # returns 1 if error
-
- def attribute_scattergram(self, i, fieldx, fieldy, output, trendline=False, callback=None):
- """Creates a scattergram for two field values of a vector's attribute table.
-
- Keyword arguments:
-
- i -- Input raster file.
- fieldx -- Input field name in attribute table for the x-axis.
- fieldy -- Input field name in attribute table for the y-axis.
- output -- Output HTML file (default name will be based on input file if unspecified).
- trendline -- Draw the trendline.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--fieldx='{}'".format(fieldx))
- args.append("--fieldy='{}'".format(fieldy))
- args.append("--output='{}'".format(output))
- if trendline: args.append("--trendline")
- return self.run_tool('attribute_scattergram', args, callback) # returns 1 if error
-
- def ceil(self, i, output, callback=None):
- """Returns the smallest (closest to negative infinity) value that is greater than or equal to the values in a raster.
-
- Keyword arguments:
-
- i -- Input raster file.
- output -- Output raster file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- return self.run_tool('ceil', args, callback) # returns 1 if error
-
- def cos(self, i, output, callback=None):
- """Returns the cosine (cos) of each values in a raster.
-
- Keyword arguments:
-
- i -- Input raster file.
- output -- Output raster file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- return self.run_tool('cos', args, callback) # returns 1 if error
-
- def cosh(self, i, output, callback=None):
- """Returns the hyperbolic cosine (cosh) of each values in a raster.
-
- Keyword arguments:
-
- i -- Input raster file.
- output -- Output raster file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- return self.run_tool('cosh', args, callback) # returns 1 if error
-
- def crispness_index(self, i, output=None, callback=None):
- """Calculates the Crispness Index, which is used to quantify how crisp (or conversely how fuzzy) a probability image is.
-
- Keyword arguments:
-
- i -- Input raster file.
- output -- Optional output html file (default name will be based on input file if unspecified).
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- if output is not None: args.append("--output='{}'".format(output))
- return self.run_tool('crispness_index', args, callback) # returns 1 if error
-
- def cross_tabulation(self, input1, input2, output, callback=None):
- """Performs a cross-tabulation on two categorical images.
-
- Keyword arguments:
-
- input1 -- Input raster file 1.
- input2 -- Input raster file 1.
- output -- Output HTML file (default name will be based on input file if unspecified).
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input1='{}'".format(input1))
- args.append("--input2='{}'".format(input2))
- args.append("--output='{}'".format(output))
- return self.run_tool('cross_tabulation', args, callback) # returns 1 if error
-
- def cumulative_distribution(self, i, output, callback=None):
- """Converts a raster image to its cumulative distribution function.
-
- Keyword arguments:
-
- i -- Input raster file.
- output -- Output raster file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- return self.run_tool('cumulative_distribution', args, callback) # returns 1 if error
-
- def decrement(self, i, output, callback=None):
- """Decreases the values of each grid cell in an input raster by 1.0 (see also InPlaceSubtract).
-
- Keyword arguments:
-
- i -- Input raster file.
- output -- Output raster file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- return self.run_tool('decrement', args, callback) # returns 1 if error
-
- def divide(self, input1, input2, output, callback=None):
- """Performs a division operation on two rasters or a raster and a constant value.
-
- Keyword arguments:
-
- input1 -- Input raster file or constant value.
- input2 -- Input raster file or constant value.
- output -- Output raster file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input1='{}'".format(input1))
- args.append("--input2='{}'".format(input2))
- args.append("--output='{}'".format(output))
- return self.run_tool('divide', args, callback) # returns 1 if error
-
- def equal_to(self, input1, input2, output, callback=None):
- """Performs a equal-to comparison operation on two rasters or a raster and a constant value.
-
- Keyword arguments:
-
- input1 -- Input raster file or constant value.
- input2 -- Input raster file or constant value.
- output -- Output raster file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input1='{}'".format(input1))
- args.append("--input2='{}'".format(input2))
- args.append("--output='{}'".format(output))
- return self.run_tool('equal_to', args, callback) # returns 1 if error
-
- def exp(self, i, output, callback=None):
- """Returns the exponential (base e) of values in a raster.
-
- Keyword arguments:
-
- i -- Input raster file.
- output -- Output raster file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- return self.run_tool('exp', args, callback) # returns 1 if error
-
- def exp2(self, i, output, callback=None):
- """Returns the exponential (base 2) of values in a raster.
-
- Keyword arguments:
-
- i -- Input raster file.
- output -- Output raster file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- return self.run_tool('exp2', args, callback) # returns 1 if error
-
- def extract_raster_statistics(self, i, features, output=None, stat="average", out_table=None, callback=None):
- """Extracts descriptive statistics for a group of patches in a raster.
-
- Keyword arguments:
-
- i -- Input data raster file.
- features -- Input feature definition raster file.
- output -- Output raster file.
- stat -- Statistic to extract, including 'average', 'minimum', 'maximum', 'range', 'standard deviation', and 'total'.
- out_table -- Output HTML Table file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--features='{}'".format(features))
- if output is not None: args.append("--output='{}'".format(output))
- args.append("--stat={}".format(stat))
- if out_table is not None: args.append("--out_table='{}'".format(out_table))
- return self.run_tool('extract_raster_statistics', args, callback) # returns 1 if error
-
- def floor(self, i, output, callback=None):
- """Returns the largest (closest to positive infinity) value that is less than or equal to the values in a raster.
-
- Keyword arguments:
-
- i -- Input raster file.
- output -- Output raster file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- return self.run_tool('floor', args, callback) # returns 1 if error
-
- def greater_than(self, input1, input2, output, incl_equals=False, callback=None):
- """Performs a greater-than comparison operation on two rasters or a raster and a constant value.
-
- Keyword arguments:
-
- input1 -- Input raster file or constant value.
- input2 -- Input raster file or constant value.
- output -- Output raster file.
- incl_equals -- Perform a greater-than-or-equal-to operation.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input1='{}'".format(input1))
- args.append("--input2='{}'".format(input2))
- args.append("--output='{}'".format(output))
- if incl_equals: args.append("--incl_equals")
- return self.run_tool('greater_than', args, callback) # returns 1 if error
-
- def image_autocorrelation(self, inputs, output, contiguity="Rook", callback=None):
- """Performs Moran's I analysis on two or more input images.
-
- Keyword arguments:
-
- inputs -- Input raster files.
- contiguity -- Contiguity type.
- output -- Output HTML file (default name will be based on input file if unspecified).
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--inputs='{}'".format(inputs))
- args.append("--contiguity={}".format(contiguity))
- args.append("--output='{}'".format(output))
- return self.run_tool('image_autocorrelation', args, callback) # returns 1 if error
-
- def image_correlation(self, inputs, output=None, callback=None):
- """Performs image correlation on two or more input images.
-
- Keyword arguments:
-
- inputs -- Input raster files.
- output -- Output HTML file (default name will be based on input file if unspecified).
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--inputs='{}'".format(inputs))
- if output is not None: args.append("--output='{}'".format(output))
- return self.run_tool('image_correlation', args, callback) # returns 1 if error
-
- def image_regression(self, input1, input2, output, out_residuals=None, standardize=False, callback=None):
- """Performs image regression analysis on two input images.
-
- Keyword arguments:
-
- input1 -- Input raster file (independent variable, X).
- input2 -- Input raster file (dependent variable, Y).
- output -- Output HTML file for regression summary report.
- out_residuals -- Output raster regression resdidual file.
- standardize -- Optional flag indicating whether to standardize the residuals map.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input1='{}'".format(input1))
- args.append("--input2='{}'".format(input2))
- args.append("--output='{}'".format(output))
- if out_residuals is not None: args.append("--out_residuals='{}'".format(out_residuals))
- if standardize: args.append("--standardize")
- return self.run_tool('image_regression', args, callback) # returns 1 if error
-
- def in_place_add(self, input1, input2, callback=None):
- """Performs an in-place addition operation (input1 += input2).
-
- Keyword arguments:
-
- input1 -- Input raster file.
- input2 -- Input raster file or constant value.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input1='{}'".format(input1))
- args.append("--input2='{}'".format(input2))
- return self.run_tool('in_place_add', args, callback) # returns 1 if error
-
- def in_place_divide(self, input1, input2, callback=None):
- """Performs an in-place division operation (input1 /= input2).
-
- Keyword arguments:
-
- input1 -- Input raster file.
- input2 -- Input raster file or constant value.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input1='{}'".format(input1))
- args.append("--input2='{}'".format(input2))
- return self.run_tool('in_place_divide', args, callback) # returns 1 if error
-
- def in_place_multiply(self, input1, input2, callback=None):
- """Performs an in-place multiplication operation (input1 *= input2).
-
- Keyword arguments:
-
- input1 -- Input raster file.
- input2 -- Input raster file or constant value.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input1='{}'".format(input1))
- args.append("--input2='{}'".format(input2))
- return self.run_tool('in_place_multiply', args, callback) # returns 1 if error
-
- def in_place_subtract(self, input1, input2, callback=None):
- """Performs an in-place subtraction operation (input1 -= input2).
-
- Keyword arguments:
-
- input1 -- Input raster file.
- input2 -- Input raster file or constant value.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input1='{}'".format(input1))
- args.append("--input2='{}'".format(input2))
- return self.run_tool('in_place_subtract', args, callback) # returns 1 if error
-
- def increment(self, i, output, callback=None):
- """Increases the values of each grid cell in an input raster by 1.0. (see also InPlaceAdd).
-
- Keyword arguments:
-
- i -- Input raster file.
- output -- Output raster file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- return self.run_tool('increment', args, callback) # returns 1 if error
-
- def integer_division(self, input1, input2, output, callback=None):
- """Performs an integer division operation on two rasters or a raster and a constant value.
-
- Keyword arguments:
-
- input1 -- Input raster file or constant value.
- input2 -- Input raster file or constant value.
- output -- Output raster file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input1='{}'".format(input1))
- args.append("--input2='{}'".format(input2))
- args.append("--output='{}'".format(output))
- return self.run_tool('integer_division', args, callback) # returns 1 if error
-
- def is_no_data(self, i, output, callback=None):
- """Identifies NoData valued pixels in an image.
-
- Keyword arguments:
-
- i -- Input raster file.
- output -- Output raster file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- return self.run_tool('is_no_data', args, callback) # returns 1 if error
-
- def kappa_index(self, input1, input2, output, callback=None):
- """Performs a kappa index of agreement (KIA) analysis on two categorical raster files.
-
- Keyword arguments:
-
- input1 -- Input classification raster file.
- input2 -- Input reference raster file.
- output -- Output HTML file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input1='{}'".format(input1))
- args.append("--input2='{}'".format(input2))
- args.append("--output='{}'".format(output))
- return self.run_tool('kappa_index', args, callback) # returns 1 if error
-
- def ks_test_for_normality(self, i, output, num_samples=None, callback=None):
- """Evaluates whether the values in a raster are normally distributed.
-
- Keyword arguments:
-
- i -- Input raster file.
- output -- Output HTML file.
- num_samples -- Number of samples. Leave blank to use whole image.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- if num_samples is not None: args.append("--num_samples='{}'".format(num_samples))
- return self.run_tool('ks_test_for_normality', args, callback) # returns 1 if error
-
- def less_than(self, input1, input2, output, incl_equals=False, callback=None):
- """Performs a less-than comparison operation on two rasters or a raster and a constant value.
-
- Keyword arguments:
-
- input1 -- Input raster file or constant value.
- input2 -- Input raster file or constant value.
- output -- Output raster file.
- incl_equals -- Perform a less-than-or-equal-to operation.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input1='{}'".format(input1))
- args.append("--input2='{}'".format(input2))
- args.append("--output='{}'".format(output))
- if incl_equals: args.append("--incl_equals")
- return self.run_tool('less_than', args, callback) # returns 1 if error
-
- def list_unique_values(self, i, field, output, callback=None):
- """Lists the unique values contained in a field witin a vector's attribute table.
-
- Keyword arguments:
-
- i -- Input raster file.
- field -- Input field name in attribute table.
- output -- Output HTML file (default name will be based on input file if unspecified).
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--field='{}'".format(field))
- args.append("--output='{}'".format(output))
- return self.run_tool('list_unique_values', args, callback) # returns 1 if error
-
- def ln(self, i, output, callback=None):
- """Returns the natural logarithm of values in a raster.
-
- Keyword arguments:
-
- i -- Input raster file.
- output -- Output raster file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- return self.run_tool('ln', args, callback) # returns 1 if error
-
- def log10(self, i, output, callback=None):
- """Returns the base-10 logarithm of values in a raster.
-
- Keyword arguments:
-
- i -- Input raster file.
- output -- Output raster file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- return self.run_tool('log10', args, callback) # returns 1 if error
-
- def log2(self, i, output, callback=None):
- """Returns the base-2 logarithm of values in a raster.
-
- Keyword arguments:
-
- i -- Input raster file.
- output -- Output raster file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- return self.run_tool('log2', args, callback) # returns 1 if error
-
- def max(self, input1, input2, output, callback=None):
- """Performs a MAX operation on two rasters or a raster and a constant value.
-
- Keyword arguments:
-
- input1 -- Input raster file or constant value.
- input2 -- Input raster file or constant value.
- output -- Output raster file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input1='{}'".format(input1))
- args.append("--input2='{}'".format(input2))
- args.append("--output='{}'".format(output))
- return self.run_tool('max', args, callback) # returns 1 if error
-
- def min(self, input1, input2, output, callback=None):
- """Performs a MIN operation on two rasters or a raster and a constant value.
-
- Keyword arguments:
-
- input1 -- Input raster file or constant value.
- input2 -- Input raster file or constant value.
- output -- Output raster file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input1='{}'".format(input1))
- args.append("--input2='{}'".format(input2))
- args.append("--output='{}'".format(output))
- return self.run_tool('min', args, callback) # returns 1 if error
-
- def modulo(self, input1, input2, output, callback=None):
- """Performs a modulo operation on two rasters or a raster and a constant value.
-
- Keyword arguments:
-
- input1 -- Input raster file or constant value.
- input2 -- Input raster file or constant value.
- output -- Output raster file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input1='{}'".format(input1))
- args.append("--input2='{}'".format(input2))
- args.append("--output='{}'".format(output))
- return self.run_tool('modulo', args, callback) # returns 1 if error
-
- def multiply(self, input1, input2, output, callback=None):
- """Performs a multiplication operation on two rasters or a raster and a constant value.
-
- Keyword arguments:
-
- input1 -- Input raster file or constant value.
- input2 -- Input raster file or constant value.
- output -- Output raster file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input1='{}'".format(input1))
- args.append("--input2='{}'".format(input2))
- args.append("--output='{}'".format(output))
- return self.run_tool('multiply', args, callback) # returns 1 if error
-
- def negate(self, i, output, callback=None):
- """Changes the sign of values in a raster or the 0-1 values of a Boolean raster.
-
- Keyword arguments:
-
- i -- Input raster file.
- output -- Output raster file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- return self.run_tool('negate', args, callback) # returns 1 if error
-
- def not_equal_to(self, input1, input2, output, callback=None):
- """Performs a not-equal-to comparison operation on two rasters or a raster and a constant value.
-
- Keyword arguments:
-
- input1 -- Input raster file or constant value.
- input2 -- Input raster file or constant value.
- output -- Output raster file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input1='{}'".format(input1))
- args.append("--input2='{}'".format(input2))
- args.append("--output='{}'".format(output))
- return self.run_tool('not_equal_to', args, callback) # returns 1 if error
-
- def power(self, input1, input2, output, callback=None):
- """Raises the values in grid cells of one rasters, or a constant value, by values in another raster or constant value.
-
- Keyword arguments:
-
- input1 -- Input raster file or constant value.
- input2 -- Input raster file or constant value.
- output -- Output raster file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input1='{}'".format(input1))
- args.append("--input2='{}'".format(input2))
- args.append("--output='{}'".format(output))
- return self.run_tool('power', args, callback) # returns 1 if error
-
- def principal_component_analysis(self, inputs, output, num_comp=None, standardized=False, callback=None):
- """Performs a principal component analysis (PCA) on a multi-spectral dataset.
-
- Keyword arguments:
-
- inputs -- Input raster files.
- output -- Output HTML report file.
- num_comp -- Number of component images to output; <= to num. input images.
- standardized -- Perform standardized PCA?.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--inputs='{}'".format(inputs))
- args.append("--output='{}'".format(output))
- if num_comp is not None: args.append("--num_comp='{}'".format(num_comp))
- if standardized: args.append("--standardized")
- return self.run_tool('principal_component_analysis', args, callback) # returns 1 if error
-
- def quantiles(self, i, output, num_quantiles=5, callback=None):
- """Transforms raster values into quantiles.
-
- Keyword arguments:
-
- i -- Input raster file.
- output -- Output raster file.
- num_quantiles -- Number of quantiles.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- args.append("--num_quantiles={}".format(num_quantiles))
- return self.run_tool('quantiles', args, callback) # returns 1 if error
-
- def random_field(self, base, output, callback=None):
- """Creates an image containing random values.
-
- Keyword arguments:
-
- base -- Input raster file.
- output -- Output raster file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--base='{}'".format(base))
- args.append("--output='{}'".format(output))
- return self.run_tool('random_field', args, callback) # returns 1 if error
-
- def random_sample(self, base, output, num_samples=1000, callback=None):
- """Creates an image containing randomly located sample grid cells with unique IDs.
-
- Keyword arguments:
-
- base -- Input raster file.
- output -- Output raster file.
- num_samples -- Number of samples.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--base='{}'".format(base))
- args.append("--output='{}'".format(output))
- args.append("--num_samples={}".format(num_samples))
- return self.run_tool('random_sample', args, callback) # returns 1 if error
-
- def raster_histogram(self, i, output, callback=None):
- """Creates a histogram from raster values.
-
- Keyword arguments:
-
- i -- Input raster file.
- output -- Output HTML file (default name will be based on input file if unspecified).
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- return self.run_tool('raster_histogram', args, callback) # returns 1 if error
-
- def raster_summary_stats(self, i, callback=None):
- """Measures a rasters min, max, average, standard deviation, num. non-nodata cells, and total.
-
- Keyword arguments:
-
- i -- Input raster file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- return self.run_tool('raster_summary_stats', args, callback) # returns 1 if error
-
- def reciprocal(self, i, output, callback=None):
- """Returns the reciprocal (i.e. 1 / z) of values in a raster.
-
- Keyword arguments:
-
- i -- Input raster file.
- output -- Output raster file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- return self.run_tool('reciprocal', args, callback) # returns 1 if error
-
- def rescale_value_range(self, i, output, out_min_val, out_max_val, clip_min=None, clip_max=None, callback=None):
- """Performs a min-max contrast stretch on an input greytone image.
-
- Keyword arguments:
-
- i -- Input raster file.
- output -- Output raster file.
- out_min_val -- New minimum value in output image.
- out_max_val -- New maximum value in output image.
- clip_min -- Optional lower tail clip value.
- clip_max -- Optional upper tail clip value.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- args.append("--out_min_val='{}'".format(out_min_val))
- args.append("--out_max_val='{}'".format(out_max_val))
- if clip_min is not None: args.append("--clip_min='{}'".format(clip_min))
- if clip_max is not None: args.append("--clip_max='{}'".format(clip_max))
- return self.run_tool('rescale_value_range', args, callback) # returns 1 if error
-
- def root_mean_square_error(self, i, base, callback=None):
- """Calculates the RMSE and other accuracy statistics.
-
- Keyword arguments:
-
- i -- Input raster file.
- base -- Input base raster file used for comparison.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--base='{}'".format(base))
- return self.run_tool('root_mean_square_error', args, callback) # returns 1 if error
-
- def round(self, i, output, callback=None):
- """Rounds the values in an input raster to the nearest integer value.
-
- Keyword arguments:
-
- i -- Input raster file.
- output -- Output raster file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- return self.run_tool('round', args, callback) # returns 1 if error
-
- def sin(self, i, output, callback=None):
- """Returns the sine (sin) of each values in a raster.
-
- Keyword arguments:
-
- i -- Input raster file.
- output -- Output raster file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- return self.run_tool('sin', args, callback) # returns 1 if error
-
- def sinh(self, i, output, callback=None):
- """Returns the hyperbolic sine (sinh) of each values in a raster.
-
- Keyword arguments:
-
- i -- Input raster file.
- output -- Output raster file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- return self.run_tool('sinh', args, callback) # returns 1 if error
-
- def square(self, i, output, callback=None):
- """Squares the values in a raster.
-
- Keyword arguments:
-
- i -- Input raster file.
- output -- Output raster file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- return self.run_tool('square', args, callback) # returns 1 if error
-
- def square_root(self, i, output, callback=None):
- """Returns the square root of the values in a raster.
-
- Keyword arguments:
-
- i -- Input raster file.
- output -- Output raster file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- return self.run_tool('square_root', args, callback) # returns 1 if error
-
- def subtract(self, input1, input2, output, callback=None):
- """Performs a differencing operation on two rasters or a raster and a constant value.
-
- Keyword arguments:
-
- input1 -- Input raster file or constant value.
- input2 -- Input raster file or constant value.
- output -- Output raster file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input1='{}'".format(input1))
- args.append("--input2='{}'".format(input2))
- args.append("--output='{}'".format(output))
- return self.run_tool('subtract', args, callback) # returns 1 if error
-
- def tan(self, i, output, callback=None):
- """Returns the tangent (tan) of each values in a raster.
-
- Keyword arguments:
-
- i -- Input raster file.
- output -- Output raster file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- return self.run_tool('tan', args, callback) # returns 1 if error
-
- def tanh(self, i, output, callback=None):
- """Returns the hyperbolic tangent (tanh) of each values in a raster.
-
- Keyword arguments:
-
- i -- Input raster file.
- output -- Output raster file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- return self.run_tool('tanh', args, callback) # returns 1 if error
-
- def to_degrees(self, i, output, callback=None):
- """Converts a raster from radians to degrees.
-
- Keyword arguments:
-
- i -- Input raster file.
- output -- Output raster file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- return self.run_tool('to_degrees', args, callback) # returns 1 if error
-
- def to_radians(self, i, output, callback=None):
- """Converts a raster from degrees to radians.
-
- Keyword arguments:
-
- i -- Input raster file.
- output -- Output raster file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- return self.run_tool('to_radians', args, callback) # returns 1 if error
-
- def trend_surface(self, i, output, order=1, callback=None):
- """Estimates the trend surface of an input raster file.
-
- Keyword arguments:
-
- i -- Input raster file.
- output -- Output raster file.
- order -- Polynomial order (1 to 10).
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- args.append("--order={}".format(order))
- return self.run_tool('trend_surface', args, callback) # returns 1 if error
-
- def trend_surface_vector_points(self, i, field, output, cell_size, order=1, callback=None):
- """Estimates a trend surface from vector points.
-
- Keyword arguments:
-
- i -- Input vector Points file.
- field -- Input field name in attribute table.
- output -- Output raster file.
- order -- Polynomial order (1 to 10).
- cell_size -- Optionally specified cell size of output raster. Not used when base raster is specified.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--field='{}'".format(field))
- args.append("--output='{}'".format(output))
- args.append("--order={}".format(order))
- args.append("--cell_size='{}'".format(cell_size))
- return self.run_tool('trend_surface_vector_points', args, callback) # returns 1 if error
-
- def truncate(self, i, output, num_decimals=None, callback=None):
- """Truncates the values in a raster to the desired number of decimal places.
-
- Keyword arguments:
-
- i -- Input raster file.
- output -- Output raster file.
- num_decimals -- Number of decimals left after truncation (default is zero).
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- if num_decimals is not None: args.append("--num_decimals='{}'".format(num_decimals))
- return self.run_tool('truncate', args, callback) # returns 1 if error
-
- def turning_bands_simulation(self, base, output, range, iterations=1000, callback=None):
- """Creates an image containing random values based on a turning-bands simulation.
-
- Keyword arguments:
-
- base -- Input base raster file.
- output -- Output file.
- range -- The field's range, in xy-units, related to the extent of spatial autocorrelation.
- iterations -- The number of iterations.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--base='{}'".format(base))
- args.append("--output='{}'".format(output))
- args.append("--range='{}'".format(range))
- args.append("--iterations={}".format(iterations))
- return self.run_tool('turning_bands_simulation', args, callback) # returns 1 if error
-
- def xor(self, input1, input2, output, callback=None):
- """Performs a logical XOR operator on two Boolean raster images.
-
- Keyword arguments:
-
- input1 -- Input raster file.
- input2 -- Input raster file.
- output -- Output raster file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input1='{}'".format(input1))
- args.append("--input2='{}'".format(input2))
- args.append("--output='{}'".format(output))
- return self.run_tool('xor', args, callback) # returns 1 if error
-
- def z_scores(self, i, output, callback=None):
- """Standardizes the values in an input raster by converting to z-scores.
-
- Keyword arguments:
-
- i -- Input raster file.
- output -- Output raster file.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--input='{}'".format(i))
- args.append("--output='{}'".format(output))
- return self.run_tool('z_scores', args, callback) # returns 1 if error
-
- ###########################
- # Stream Network Analysis #
- ###########################
-
- def distance_to_outlet(self, d8_pntr, streams, output, esri_pntr=False, zero_background=False, callback=None):
- """Calculates the distance of stream grid cells to the channel network outlet cell.
-
- Keyword arguments:
-
- d8_pntr -- Input raster D8 pointer file.
- streams -- Input raster streams file.
- output -- Output raster file.
- esri_pntr -- D8 pointer uses the ESRI style scheme.
- zero_background -- Flag indicating whether a background value of zero should be used.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--d8_pntr='{}'".format(d8_pntr))
- args.append("--streams='{}'".format(streams))
- args.append("--output='{}'".format(output))
- if esri_pntr: args.append("--esri_pntr")
- if zero_background: args.append("--zero_background")
- return self.run_tool('distance_to_outlet', args, callback) # returns 1 if error
-
- def extract_streams(self, flow_accum, output, threshold, zero_background=False, callback=None):
- """Extracts stream grid cells from a flow accumulation raster.
-
- Keyword arguments:
-
- flow_accum -- Input raster D8 flow accumulation file.
- output -- Output raster file.
- threshold -- Threshold in flow accumulation values for channelization.
- zero_background -- Flag indicating whether a background value of zero should be used.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--flow_accum='{}'".format(flow_accum))
- args.append("--output='{}'".format(output))
- args.append("--threshold='{}'".format(threshold))
- if zero_background: args.append("--zero_background")
- return self.run_tool('extract_streams', args, callback) # returns 1 if error
-
- def extract_valleys(self, dem, output, variant="Lower Quartile", line_thin=True, filter=5, callback=None):
- """Identifies potential valley bottom grid cells based on local topolography alone.
-
- Keyword arguments:
-
- dem -- Input raster DEM file.
- output -- Output raster file.
- variant -- Options include 'lq' (lower quartile), 'JandR' (Johnston and Rosenfeld), and 'PandD' (Peucker and Douglas); default is 'lq'.
- line_thin -- Optional flag indicating whether post-processing line-thinning should be performed.
- filter -- Optional argument (only used when variant='lq') providing the filter size, in grid cells, used for lq-filtering (default is 5).
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--dem='{}'".format(dem))
- args.append("--output='{}'".format(output))
- args.append("--variant={}".format(variant))
- if line_thin: args.append("--line_thin")
- args.append("--filter={}".format(filter))
- return self.run_tool('extract_valleys', args, callback) # returns 1 if error
-
- def farthest_channel_head(self, d8_pntr, streams, output, esri_pntr=False, zero_background=False, callback=None):
- """Calculates the distance to the furthest upstream channel head for each stream cell.
-
- Keyword arguments:
-
- d8_pntr -- Input raster D8 pointer file.
- streams -- Input raster streams file.
- output -- Output raster file.
- esri_pntr -- D8 pointer uses the ESRI style scheme.
- zero_background -- Flag indicating whether a background value of zero should be used.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--d8_pntr='{}'".format(d8_pntr))
- args.append("--streams='{}'".format(streams))
- args.append("--output='{}'".format(output))
- if esri_pntr: args.append("--esri_pntr")
- if zero_background: args.append("--zero_background")
- return self.run_tool('farthest_channel_head', args, callback) # returns 1 if error
-
- def find_main_stem(self, d8_pntr, streams, output, esri_pntr=False, zero_background=False, callback=None):
- """Finds the main stem, based on stream lengths, of each stream network.
-
- Keyword arguments:
-
- d8_pntr -- Input raster D8 pointer file.
- streams -- Input raster streams file.
- output -- Output raster file.
- esri_pntr -- D8 pointer uses the ESRI style scheme.
- zero_background -- Flag indicating whether a background value of zero should be used.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--d8_pntr='{}'".format(d8_pntr))
- args.append("--streams='{}'".format(streams))
- args.append("--output='{}'".format(output))
- if esri_pntr: args.append("--esri_pntr")
- if zero_background: args.append("--zero_background")
- return self.run_tool('find_main_stem', args, callback) # returns 1 if error
-
- def hack_stream_order(self, d8_pntr, streams, output, esri_pntr=False, zero_background=False, callback=None):
- """Assigns the Hack stream order to each tributary in a stream network.
-
- Keyword arguments:
-
- d8_pntr -- Input raster D8 pointer file.
- streams -- Input raster streams file.
- output -- Output raster file.
- esri_pntr -- D8 pointer uses the ESRI style scheme.
- zero_background -- Flag indicating whether a background value of zero should be used.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--d8_pntr='{}'".format(d8_pntr))
- args.append("--streams='{}'".format(streams))
- args.append("--output='{}'".format(output))
- if esri_pntr: args.append("--esri_pntr")
- if zero_background: args.append("--zero_background")
- return self.run_tool('hack_stream_order', args, callback) # returns 1 if error
-
- def horton_stream_order(self, d8_pntr, streams, output, esri_pntr=False, zero_background=False, callback=None):
- """Assigns the Horton stream order to each tributary in a stream network.
-
- Keyword arguments:
-
- d8_pntr -- Input raster D8 pointer file.
- streams -- Input raster streams file.
- output -- Output raster file.
- esri_pntr -- D8 pointer uses the ESRI style scheme.
- zero_background -- Flag indicating whether a background value of zero should be used.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--d8_pntr='{}'".format(d8_pntr))
- args.append("--streams='{}'".format(streams))
- args.append("--output='{}'".format(output))
- if esri_pntr: args.append("--esri_pntr")
- if zero_background: args.append("--zero_background")
- return self.run_tool('horton_stream_order', args, callback) # returns 1 if error
-
- def length_of_upstream_channels(self, d8_pntr, streams, output, esri_pntr=False, zero_background=False, callback=None):
- """Calculates the total length of channels upstream.
-
- Keyword arguments:
-
- d8_pntr -- Input raster D8 pointer file.
- streams -- Input raster streams file.
- output -- Output raster file.
- esri_pntr -- D8 pointer uses the ESRI style scheme.
- zero_background -- Flag indicating whether a background value of zero should be used.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--d8_pntr='{}'".format(d8_pntr))
- args.append("--streams='{}'".format(streams))
- args.append("--output='{}'".format(output))
- if esri_pntr: args.append("--esri_pntr")
- if zero_background: args.append("--zero_background")
- return self.run_tool('length_of_upstream_channels', args, callback) # returns 1 if error
-
- def long_profile(self, d8_pntr, streams, dem, output, esri_pntr=False, callback=None):
- """Plots the stream longitudinal profiles for one or more rivers.
-
- Keyword arguments:
-
- d8_pntr -- Input raster D8 pointer file.
- streams -- Input raster streams file.
- dem -- Input raster DEM file.
- output -- Output HTML file.
- esri_pntr -- D8 pointer uses the ESRI style scheme.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--d8_pntr='{}'".format(d8_pntr))
- args.append("--streams='{}'".format(streams))
- args.append("--dem='{}'".format(dem))
- args.append("--output='{}'".format(output))
- if esri_pntr: args.append("--esri_pntr")
- return self.run_tool('long_profile', args, callback) # returns 1 if error
-
- def long_profile_from_points(self, d8_pntr, points, dem, output, esri_pntr=False, callback=None):
- """Plots the longitudinal profiles from flow-paths initiating from a set of vector points.
-
- Keyword arguments:
-
- d8_pntr -- Input raster D8 pointer file.
- points -- Input vector points file.
- dem -- Input raster DEM file.
- output -- Output HTML file.
- esri_pntr -- D8 pointer uses the ESRI style scheme.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--d8_pntr='{}'".format(d8_pntr))
- args.append("--points='{}'".format(points))
- args.append("--dem='{}'".format(dem))
- args.append("--output='{}'".format(output))
- if esri_pntr: args.append("--esri_pntr")
- return self.run_tool('long_profile_from_points', args, callback) # returns 1 if error
-
- def raster_streams_to_vector(self, streams, d8_pntr, output, esri_pntr=False, callback=None):
- """Converts a raster stream file into a vector file.
-
- Keyword arguments:
-
- streams -- Input raster streams file.
- d8_pntr -- Input raster D8 pointer file.
- output -- Output vector file.
- esri_pntr -- D8 pointer uses the ESRI style scheme.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--streams='{}'".format(streams))
- args.append("--d8_pntr='{}'".format(d8_pntr))
- args.append("--output='{}'".format(output))
- if esri_pntr: args.append("--esri_pntr")
- return self.run_tool('raster_streams_to_vector', args, callback) # returns 1 if error
-
- def rasterize_streams(self, streams, base, output, nodata=True, feature_id=False, callback=None):
- """Rasterizes vector streams based on Lindsay (2016) method.
-
- Keyword arguments:
-
- streams -- Input vector streams file.
- base -- Input base raster file.
- output -- Output raster file.
- nodata -- Use NoData value for background?.
- feature_id -- Use feature number as output value?.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--streams='{}'".format(streams))
- args.append("--base='{}'".format(base))
- args.append("--output='{}'".format(output))
- if nodata: args.append("--nodata")
- if feature_id: args.append("--feature_id")
- return self.run_tool('rasterize_streams', args, callback) # returns 1 if error
-
- def remove_short_streams(self, d8_pntr, streams, output, min_length, esri_pntr=False, callback=None):
- """Removes short first-order streams from a stream network.
-
- Keyword arguments:
-
- d8_pntr -- Input raster D8 pointer file.
- streams -- Input raster streams file.
- output -- Output raster file.
- min_length -- Minimum tributary length (in map units) used for network prunning.
- esri_pntr -- D8 pointer uses the ESRI style scheme.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--d8_pntr='{}'".format(d8_pntr))
- args.append("--streams='{}'".format(streams))
- args.append("--output='{}'".format(output))
- args.append("--min_length='{}'".format(min_length))
- if esri_pntr: args.append("--esri_pntr")
- return self.run_tool('remove_short_streams', args, callback) # returns 1 if error
-
- def shreve_stream_magnitude(self, d8_pntr, streams, output, esri_pntr=False, zero_background=False, callback=None):
- """Assigns the Shreve stream magnitude to each link in a stream network.
-
- Keyword arguments:
-
- d8_pntr -- Input raster D8 pointer file.
- streams -- Input raster streams file.
- output -- Output raster file.
- esri_pntr -- D8 pointer uses the ESRI style scheme.
- zero_background -- Flag indicating whether a background value of zero should be used.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--d8_pntr='{}'".format(d8_pntr))
- args.append("--streams='{}'".format(streams))
- args.append("--output='{}'".format(output))
- if esri_pntr: args.append("--esri_pntr")
- if zero_background: args.append("--zero_background")
- return self.run_tool('shreve_stream_magnitude', args, callback) # returns 1 if error
-
- def strahler_stream_order(self, d8_pntr, streams, output, esri_pntr=False, zero_background=False, callback=None):
- """Assigns the Strahler stream order to each link in a stream network.
-
- Keyword arguments:
-
- d8_pntr -- Input raster D8 pointer file.
- streams -- Input raster streams file.
- output -- Output raster file.
- esri_pntr -- D8 pointer uses the ESRI style scheme.
- zero_background -- Flag indicating whether a background value of zero should be used.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--d8_pntr='{}'".format(d8_pntr))
- args.append("--streams='{}'".format(streams))
- args.append("--output='{}'".format(output))
- if esri_pntr: args.append("--esri_pntr")
- if zero_background: args.append("--zero_background")
- return self.run_tool('strahler_stream_order', args, callback) # returns 1 if error
-
- def stream_link_class(self, d8_pntr, streams, output, esri_pntr=False, zero_background=False, callback=None):
- """Identifies the exterior/interior links and nodes in a stream network.
-
- Keyword arguments:
-
- d8_pntr -- Input raster D8 pointer file.
- streams -- Input raster streams file.
- output -- Output raster file.
- esri_pntr -- D8 pointer uses the ESRI style scheme.
- zero_background -- Flag indicating whether a background value of zero should be used.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--d8_pntr='{}'".format(d8_pntr))
- args.append("--streams='{}'".format(streams))
- args.append("--output='{}'".format(output))
- if esri_pntr: args.append("--esri_pntr")
- if zero_background: args.append("--zero_background")
- return self.run_tool('stream_link_class', args, callback) # returns 1 if error
-
- def stream_link_identifier(self, d8_pntr, streams, output, esri_pntr=False, zero_background=False, callback=None):
- """Assigns a unique identifier to each link in a stream network.
-
- Keyword arguments:
-
- d8_pntr -- Input raster D8 pointer file.
- streams -- Input raster streams file.
- output -- Output raster file.
- esri_pntr -- D8 pointer uses the ESRI style scheme.
- zero_background -- Flag indicating whether a background value of zero should be used.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--d8_pntr='{}'".format(d8_pntr))
- args.append("--streams='{}'".format(streams))
- args.append("--output='{}'".format(output))
- if esri_pntr: args.append("--esri_pntr")
- if zero_background: args.append("--zero_background")
- return self.run_tool('stream_link_identifier', args, callback) # returns 1 if error
-
- def stream_link_length(self, d8_pntr, linkid, output, esri_pntr=False, zero_background=False, callback=None):
- """Estimates the length of each link (or tributary) in a stream network.
-
- Keyword arguments:
-
- d8_pntr -- Input raster D8 pointer file.
- linkid -- Input raster streams link ID (or tributary ID) file.
- output -- Output raster file.
- esri_pntr -- D8 pointer uses the ESRI style scheme.
- zero_background -- Flag indicating whether a background value of zero should be used.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--d8_pntr='{}'".format(d8_pntr))
- args.append("--linkid='{}'".format(linkid))
- args.append("--output='{}'".format(output))
- if esri_pntr: args.append("--esri_pntr")
- if zero_background: args.append("--zero_background")
- return self.run_tool('stream_link_length', args, callback) # returns 1 if error
-
- def stream_link_slope(self, d8_pntr, linkid, dem, output, esri_pntr=False, zero_background=False, callback=None):
- """Estimates the average slope of each link (or tributary) in a stream network.
-
- Keyword arguments:
-
- d8_pntr -- Input raster D8 pointer file.
- linkid -- Input raster streams link ID (or tributary ID) file.
- dem -- Input raster DEM file.
- output -- Output raster file.
- esri_pntr -- D8 pointer uses the ESRI style scheme.
- zero_background -- Flag indicating whether a background value of zero should be used.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--d8_pntr='{}'".format(d8_pntr))
- args.append("--linkid='{}'".format(linkid))
- args.append("--dem='{}'".format(dem))
- args.append("--output='{}'".format(output))
- if esri_pntr: args.append("--esri_pntr")
- if zero_background: args.append("--zero_background")
- return self.run_tool('stream_link_slope', args, callback) # returns 1 if error
-
- def stream_slope_continuous(self, d8_pntr, streams, dem, output, esri_pntr=False, zero_background=False, callback=None):
- """Estimates the slope of each grid cell in a stream network.
-
- Keyword arguments:
-
- d8_pntr -- Input raster D8 pointer file.
- streams -- Input raster streams file.
- dem -- Input raster DEM file.
- output -- Output raster file.
- esri_pntr -- D8 pointer uses the ESRI style scheme.
- zero_background -- Flag indicating whether a background value of zero should be used.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--d8_pntr='{}'".format(d8_pntr))
- args.append("--streams='{}'".format(streams))
- args.append("--dem='{}'".format(dem))
- args.append("--output='{}'".format(output))
- if esri_pntr: args.append("--esri_pntr")
- if zero_background: args.append("--zero_background")
- return self.run_tool('stream_slope_continuous', args, callback) # returns 1 if error
-
- def topological_stream_order(self, d8_pntr, streams, output, esri_pntr=False, zero_background=False, callback=None):
- """Assigns each link in a stream network its topological order.
-
- Keyword arguments:
-
- d8_pntr -- Input raster D8 pointer file.
- streams -- Input raster streams file.
- output -- Output raster file.
- esri_pntr -- D8 pointer uses the ESRI style scheme.
- zero_background -- Flag indicating whether a background value of zero should be used.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--d8_pntr='{}'".format(d8_pntr))
- args.append("--streams='{}'".format(streams))
- args.append("--output='{}'".format(output))
- if esri_pntr: args.append("--esri_pntr")
- if zero_background: args.append("--zero_background")
- return self.run_tool('topological_stream_order', args, callback) # returns 1 if error
-
- def tributary_identifier(self, d8_pntr, streams, output, esri_pntr=False, zero_background=False, callback=None):
- """Assigns a unique identifier to each tributary in a stream network.
-
- Keyword arguments:
-
- d8_pntr -- Input raster D8 pointer file.
- streams -- Input raster streams file.
- output -- Output raster file.
- esri_pntr -- D8 pointer uses the ESRI style scheme.
- zero_background -- Flag indicating whether a background value of zero should be used.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--d8_pntr='{}'".format(d8_pntr))
- args.append("--streams='{}'".format(streams))
- args.append("--output='{}'".format(output))
- if esri_pntr: args.append("--esri_pntr")
- if zero_background: args.append("--zero_background")
- return self.run_tool('tributary_identifier', args, callback) # returns 1 if error
diff --git a/wb_runner.py b/wb_runner.py
index 4937a9b21..167e3bd5f 100644
--- a/wb_runner.py
+++ b/wb_runner.py
@@ -1,9 +1,9 @@
#!/usr/bin/env python3
# This script is part of the WhiteboxTools geospatial analysis library.
-# Authors: Dr. John Lindsay
-# Created: November 28, 2017
-# Last Modified: November 28, 2017
+# Authors: Dr. John Lindsay, Rachel Broders
+# Created: 28/11/2017
+# Last Modified: 05/11/2019
# License: MIT
import __future__
@@ -16,6 +16,7 @@
# from __future__ import print_function
# from enum import Enum
import platform
+import re #Added by Rachel for snake_to_camel function
from pathlib import Path
import glob
from sys import platform as _platform
@@ -25,6 +26,7 @@
from tkinter.scrolledtext import ScrolledText
from tkinter import filedialog
from tkinter import messagebox
+from tkinter import PhotoImage
import webbrowser
from whitebox_tools import WhiteboxTools, to_camelcase
@@ -49,7 +51,7 @@ def __init__(self, json_str, runner, master=None):
self.runner = runner
- ttk.Frame.__init__(self, master, padding='0.1i')
+ ttk.Frame.__init__(self, master, padding='0.02i')
self.grid()
self.label = ttk.Label(self, text=self.name, justify=tk.LEFT)
@@ -68,10 +70,12 @@ def __init__(self, json_str, runner, master=None):
if default_value:
self.value.set(default_value)
- # self.img = tk.PhotoImage(file=script_dir + "/img/open.gif")
- # self.open_button = ttk.Button(fs_frame, width=55, image=self.img, command=self.select_dir)
- self.open_button = ttk.Button(
- fs_frame, width=4, text="...", command=self.select_file)
+ # dir_path = os.path.dirname(os.path.realpath(__file__))
+ # print(dir_path)
+ # self.open_file_icon = tk.PhotoImage(file = dir_path + '//img//open.png') #Added by Rachel to replace file selector "..." button with open file icon
+
+ # self.open_button = ttk.Button(fs_frame, width=4, image = self.open_file_icon, command=self.select_file, padding = '0.02i')
+ self.open_button = ttk.Button(fs_frame, width=4, text="...", command=self.select_file, padding = '0.02i')
self.open_button.grid(row=0, column=1, sticky=tk.E)
self.open_button.columnconfigure(0, weight=1)
fs_frame.grid(row=1, column=0, sticky=tk.NSEW)
@@ -97,12 +101,12 @@ def select_file(self):
ftypes = [('All files', '*.*')]
if 'RasterAndVector' in self.file_type:
ftypes = [("Shapefiles", "*.shp"), ('Raster files', ('*.dep', '*.tif',
- '*.tiff', '*.gtif', '*.gtiff', '*.flt',
+ '*.tiff', '*.flt',
'*.sdat', '*.rdc',
'*.asc'))]
elif 'Raster' in self.file_type:
ftypes = [('Raster files', ('*.dep', '*.tif',
- '*.tiff', '*.gtif', '*.gtiff', '*.flt',
+ '*.tiff', '*.flt',
'*.sdat', '*.rdc',
'*.asc'))]
elif 'Lidar' in self.file_type:
@@ -195,7 +199,7 @@ def __init__(self, json_str, runner, master=None):
ttk.Frame.__init__(self, master)
self.grid()
- self['padding'] = '0.1i'
+ self['padding'] = '0.02i'
self.label = ttk.Label(self, text=self.name, justify=tk.LEFT)
self.label.grid(row=0, column=0, sticky=tk.W)
@@ -358,7 +362,7 @@ def __init__(self, json_str, runner, master=None):
ttk.Frame.__init__(self, master)
self.grid()
- self['padding'] = '0.1i'
+ self['padding'] = '0.05i'
self.label = ttk.Label(self, text=self.name, justify=tk.LEFT)
self.label.grid(row=0, column=0, sticky=tk.W)
@@ -482,7 +486,7 @@ def __init__(self, json_str, master=None):
ttk.Frame.__init__(self, master)
self.grid()
- self['padding'] = '0.1i'
+ self['padding'] = '0.05i'
frame = ttk.Frame(self, padding='0.0i')
@@ -524,7 +528,7 @@ def __init__(self, json_str, master=None):
ttk.Frame.__init__(self, master)
self.grid()
- self['padding'] = '0.1i'
+ self['padding'] = '0.02i'
frame = ttk.Frame(self, padding='0.0i')
@@ -757,11 +761,6 @@ def __init__(self, tool_name=None, master=None):
self.grid()
self.tool_name = tool_name
self.master.title("WhiteboxTools Runner")
- # widthpixels = 800
- # heightpixels = 600
- # self.master.geometry('{}x{}'.format(widthpixels, heightpixels))
- # self.master.resizable(0, 0)
- # self.master.lift()
if _platform == "darwin":
os.system(
'''/usr/bin/osascript -e 'tell app "Finder" to set frontmost of process "Python" to true' ''')
@@ -769,162 +768,423 @@ def __init__(self, tool_name=None, master=None):
self.working_dir = str(Path.home())
def create_widgets(self):
- toplevel_frame = ttk.Frame(self, padding='0.1i')
- (self.toolslist, selected_item) = self.get_tools_list()
- self.tools_frame = ttk.LabelFrame(toplevel_frame, text="{} Available Tools".format(
- len(self.toolslist)), padding='0.1i')
- self.toolnames = tk.StringVar(value=self.toolslist)
- self.tools_listbox = tk.Listbox(
- self.tools_frame, height=22, listvariable=self.toolnames)
- self.tools_listbox.bind("<>", self.update_tool_help)
- self.tools_listbox.grid(row=0, column=0, sticky=tk.NSEW)
- self.tools_listbox.columnconfigure(0, weight=10)
- self.tools_listbox.rowconfigure(0, weight=1)
- s = ttk.Scrollbar(self.tools_frame, orient=tk.VERTICAL,
- command=self.tools_listbox.yview)
- s.grid(row=0, column=1, sticky=(tk.N, tk.S))
- self.tools_listbox['yscrollcommand'] = s.set
+ #########################################################
+ # Overall/Top level Frame #
+ #########################################################
+ #define left-side frame (toplevel_frame) and right-side frame (overall_frame)
+ toplevel_frame = ttk.Frame(self, padding='0.1i')
+ overall_frame = ttk.Frame(self, padding='0.1i')
+ #set-up layout
+ overall_frame.grid(row=0, column=1, sticky=tk.NSEW)
+ toplevel_frame.grid(row=0, column=0, sticky=tk.NSEW)
+ #########################################################
+ # Calling basics #
+ #########################################################
+ #Create all needed lists of tools and toolboxes
+ self.toolbox_list = self.get_toolboxes()
+ self.sort_toolboxes()
+ self.tools_and_toolboxes = wbt.toolbox('')
+ self.sort_tools_by_toolbox()
+ self.get_tools_list()
+ #Icons to be used in tool treeview
+ self.tool_icon = tk.PhotoImage(file = self.script_dir + '//img//tool.png')
+ self.open_toolbox_icon = tk.PhotoImage(file = self.script_dir + '//img//open.png')
+ self.closed_toolbox_icon = tk.PhotoImage(file = self.script_dir + '//img//closed.png')
+ #########################################################
+ # Toolboxes Frame #FIXME: change width or make horizontally scrollable
+ #########################################################
+ #define tools_frame and tool_tree
+ self.tools_frame = ttk.LabelFrame(toplevel_frame, text="{} Available Tools".format(len(self.tools_list)), padding='0.1i')
+ self.tool_tree = ttk.Treeview(self.tools_frame, height = 21)
+ #Set up layout
+ self.tool_tree.grid(row=0, column=0, sticky=tk.NSEW)
+ self.tool_tree.column("#0", width = 280) #Set width so all tools are readable within the frame
self.tools_frame.grid(row=0, column=0, sticky=tk.NSEW)
self.tools_frame.columnconfigure(0, weight=10)
self.tools_frame.columnconfigure(1, weight=1)
- self.tools_frame.rowconfigure(0, weight=1)
-
- overall_frame = ttk.Frame(toplevel_frame, padding='0.1i')
-
- # json_str = '{"default_value": null, "description": "Directory containing data files.", "flags": ["--wd"], "name": "Working Directory", "optional": true, "parameter_type": "Directory"}'
- # self.wd = FileSelector(json_str, overall_frame)
- # self.wd.grid(row=0, column=0, sticky=tk.NSEW)
-
- current_tool_frame = ttk.Frame(overall_frame, padding='0.1i')
- self.current_tool_lbl = ttk.Label(current_tool_frame, text="Current Tool: {}".format(
- self.tool_name), justify=tk.LEFT) # , font=("Helvetica", 12, "bold")
- self.current_tool_lbl.grid(row=0, column=0, sticky=tk.W)
- self.view_code_button = ttk.Button(
- current_tool_frame, text="View Code", width=12, command=self.view_code)
+ self.tools_frame.rowconfigure(0, weight=10)
+ self.tools_frame.rowconfigure(1, weight=1)
+ #Add toolboxes and tools to treeview
+ index = 0
+ for toolbox in self.lower_toolboxes:
+ if toolbox.find('/') != (-1): #toolboxes
+ self.tool_tree.insert(toolbox[:toolbox.find('/')], 0, text = " " + toolbox[toolbox.find('/') + 1:], iid = toolbox[toolbox.find('/') + 1:], tags = 'toolbox', image = self.closed_toolbox_icon)
+ for tool in self.sorted_tools[index]: #add tools within toolbox
+ self.tool_tree.insert(toolbox[toolbox.find('/') + 1:], 'end', text = " " + tool, tags = 'tool', iid = tool, image = self.tool_icon)
+ else: #subtoolboxes
+ self.tool_tree.insert('', 'end', text = " " + toolbox, iid = toolbox, tags = 'toolbox', image = self.closed_toolbox_icon)
+ for tool in self.sorted_tools[index]: #add tools within subtoolbox
+ self.tool_tree.insert(toolbox, 'end', text = " " + tool, iid = tool, tags = 'tool', image = self.tool_icon)
+ index = index + 1
+ #bind tools in treeview to self.tree_update_tool_help function and toolboxes to self.update_toolbox_icon function
+ self.tool_tree.tag_bind('tool', "<>", self.tree_update_tool_help)
+ self.tool_tree.tag_bind('toolbox', "<>", self.update_toolbox_icon)
+ #Add vertical scrollbar to treeview frame
+ s = ttk.Scrollbar(self.tools_frame, orient=tk.VERTICAL,command=self.tool_tree.yview)
+ s.grid(row=0, column=1, sticky=(tk.N, tk.S))
+ self.tool_tree['yscrollcommand'] = s.set
+ #########################################################
+ # Search Bar #
+ #########################################################
+ #create variables for search results and search input
+ self.search_list = []
+ self.search_text = tk.StringVar()
+ #Create the elements of the search frame
+ self.search_frame = ttk.LabelFrame(toplevel_frame, padding='0.1i', text="{} Tools Found".format(len(self.search_list)))
+ self.search_label = ttk.Label(self.search_frame, text = "Search: ")
+ self.search_bar = ttk.Entry(self.search_frame, width = 30, textvariable = self.search_text)
+ self.search_results_listbox = tk.Listbox(self.search_frame, height=11)
+ self.search_scroll = ttk.Scrollbar(self.search_frame, orient=tk.VERTICAL, command=self.search_results_listbox.yview)
+ self.search_results_listbox['yscrollcommand'] = self.search_scroll.set
+ #Add bindings
+ self.search_results_listbox.bind("<>", self.search_update_tool_help)
+ self.search_bar.bind('', self.update_search)
+ #Define layout of the frame
+ self.search_frame.grid(row = 1, column = 0, sticky=tk.NSEW)
+ self.search_label.grid(row = 0, column = 0, sticky=tk.NW)
+ self.search_bar.grid(row = 0, column = 1, sticky=tk.NE)
+ self.search_results_listbox.grid(row = 1, column = 0, columnspan = 2, sticky=tk.NSEW, pady = 5)
+ self.search_scroll.grid(row=1, column=2, sticky=(tk.N, tk.S))
+ #Configure rows and columns of the frame
+ self.search_frame.columnconfigure(0, weight=1)
+ self.search_frame.columnconfigure(1, weight=10)
+ self.search_frame.columnconfigure(1, weight=1)
+ self.search_frame.rowconfigure(0, weight=1)
+ self.search_frame.rowconfigure(1, weight = 10)
+ #########################################################
+ # Current Tool Frame #
+ #########################################################
+ #Create the elements of the current tool frame
+ self.current_tool_frame = ttk.Frame(overall_frame, padding='0.01i')
+ self.current_tool_lbl = ttk.Label(self.current_tool_frame, text="Current Tool: {}".format(self.tool_name), justify=tk.LEFT) # , font=("Helvetica", 12, "bold")
+ self.view_code_button = ttk.Button(self.current_tool_frame, text="View Code", width=12, command=self.view_code)
+ #Define layout of the frame
self.view_code_button.grid(row=0, column=1, sticky=tk.E)
- current_tool_frame.grid(row=1, column=0, sticky=tk.NSEW)
- current_tool_frame.columnconfigure(0, weight=1)
- current_tool_frame.columnconfigure(1, weight=1)
-
- # tool_args_frame = ttk.Frame(overall_frame, padding='0.0i')
- self.tool_args_frame = ttk.Frame(overall_frame, padding='0.0i')
- self.tool_args_frame.grid(row=2, column=0, sticky=tk.NSEW)
- self.tool_args_frame.columnconfigure(0, weight=1)
-
- # args_frame = ttk.Frame(overall_frame, padding='0.1i')
- # self.args_label = ttk.Label(args_frame, text="Tool Arguments:", justify=tk.LEFT)
- # self.args_label.grid(row=0, column=0, sticky=tk.W)
- # args_frame2 = ttk.Frame(args_frame, padding='0.0i')
- # self.args_value = tk.StringVar()
- # self.args_text = ttk.Entry(args_frame2, width=45, justify=tk.LEFT, textvariable=self.args_value)
- # self.args_text.grid(row=0, column=0, sticky=tk.NSEW)
- # self.args_text.columnconfigure(0, weight=1)
- # self.clearButton = ttk.Button(args_frame2, text="Clear", width=4, command=self.clear_args_box)
- # self.clearButton.pack(pady=10, padx=10)
- # self.clearButton.grid(row=0, column=1, sticky=tk.E)
- # self.clearButton.columnconfigure(0, weight=1)
- # args_frame2.grid(row=1, column=0, sticky=tk.NSEW)
- # args_frame2.columnconfigure(0, weight=10)
- # args_frame2.columnconfigure(1, weight=1)
- # args_frame.grid(row=2, column=0, sticky=tk.NSEW)
- # args_frame.columnconfigure(0, weight=1)
-
- # # Add the bindings
- # if _platform == "darwin":
- # self.args_text.bind("", self.args_select_all)
- # else:
- # self.args_text.bind("", self.args_select_all)
-
- buttonsFrame = ttk.Frame(overall_frame, padding='0.1i')
- self.run_button = ttk.Button(
- buttonsFrame, text="Run", width=8, command=self.run_tool)
- # self.run_button.pack(pady=10, padx=10)
+ self.current_tool_lbl.grid(row=0, column=0, sticky=tk.W)
+ self.current_tool_frame.grid(row=0, column=0, columnspan = 2, sticky=tk.NSEW)
+ #Configure rows and columns of the frame
+ self.current_tool_frame.columnconfigure(0, weight=1)
+ self.current_tool_frame.columnconfigure(1, weight=1)
+ #########################################################
+ # Args Frame #
+ #########################################################
+ # #Create the elements of the tool arguments frame
+ # self.arg_scroll = ttk.Scrollbar(overall_frame, orient='vertical')
+ # self.arg_canvas = tk.Canvas(overall_frame, bd=0, highlightthickness=0, yscrollcommand=self.arg_scroll.set)
+ # self.arg_scroll.config(command=self.arg_canvas.yview) #self.arg_scroll scrolls over self.arg_canvas
+ # self.arg_scroll_frame = ttk.Frame(self.arg_canvas) # create a frame inside the self.arg_canvas which will be scrolled with it
+ # self.arg_scroll_frame_id = self.arg_canvas.create_window(0, 0, window=self.arg_scroll_frame, anchor="nw")
+ # #Define layout of the frame
+ # self.arg_scroll.grid(row = 1, column = 1, sticky = (tk.NS, tk.E))
+ # self.arg_canvas.grid(row = 1, column = 0, sticky = tk.NSEW)
+ # # reset the view
+ # self.arg_canvas.xview_moveto(0)
+ # self.arg_canvas.yview_moveto(0)
+ # #Add bindings
+ # self.arg_scroll_frame.bind('', self.configure_arg_scroll_frame)
+ # self.arg_canvas.bind('', self.configure_arg_canvas)
+ self.arg_scroll_frame = ttk.Frame(overall_frame, padding='0.0i')
+ self.arg_scroll_frame.grid(row=1, column=0, sticky=tk.NSEW)
+ self.arg_scroll_frame.columnconfigure(0, weight=1)
+ #########################################################
+ # Buttons Frame #
+ #########################################################
+ #Create the elements of the buttons frame
+ buttons_frame = ttk.Frame(overall_frame, padding='0.1i')
+ self.run_button = ttk.Button(buttons_frame, text="Run", width=8, command=self.run_tool)
+ self.quit_button = ttk.Button(buttons_frame, text="Cancel", width=8, command=self.cancel_operation)
+ self.help_button = ttk.Button(buttons_frame, text="Help", width=8, command=self.tool_help_button)
+ #Define layout of the frame
self.run_button.grid(row=0, column=0)
- self.quitButton = ttk.Button(
- buttonsFrame, text="Cancel", width=8, command=self.cancel_operation)
- self.quitButton.grid(row=0, column=1)
- buttonsFrame.grid(row=3, column=0, sticky=tk.E)
-
- output_frame = ttk.Frame(overall_frame, padding='0.1i')
+ self.quit_button.grid(row=0, column=1)
+ self.help_button.grid(row = 0, column = 2)
+ buttons_frame.grid(row=2, column=0, columnspan = 2, sticky=tk.E)
+ #########################################################
+ # Output Frame #
+ #########################################################
+ #Create the elements of the output frame
+ output_frame = ttk.Frame(overall_frame)
outlabel = ttk.Label(output_frame, text="Output:", justify=tk.LEFT)
- outlabel.grid(row=0, column=0, sticky=tk.NW)
- k = wbt.tool_help(self.tool_name)
- self.out_text = ScrolledText(
- output_frame, width=63, height=10, wrap=tk.NONE, padx=7, pady=7)
+ self.out_text = ScrolledText(output_frame, width=63, height=15, wrap=tk.NONE, padx=7, pady=7, exportselection = 0)
+ output_scrollbar = ttk.Scrollbar(output_frame, orient=tk.HORIZONTAL, command = self.out_text.xview)
+ self.out_text['xscrollcommand'] = output_scrollbar.set
+ #Retreive and insert the text for the current tool
+ k = wbt.tool_help(self.tool_name)
self.out_text.insert(tk.END, k)
+ #Define layout of the frame
+ outlabel.grid(row=0, column=0, sticky=tk.NW)
self.out_text.grid(row=1, column=0, sticky=tk.NSEW)
+ output_frame.grid(row=3, column=0, columnspan = 2, sticky=(tk.NS, tk.E))
+ output_scrollbar.grid(row=2, column=0, sticky=(tk.W, tk.E))
+ #Configure rows and columns of the frame
self.out_text.columnconfigure(0, weight=1)
- output_frame.grid(row=4, column=0, sticky=tk.NSEW)
output_frame.columnconfigure(0, weight=1)
-
# Add the binding
if _platform == "darwin":
self.out_text.bind("", self.select_all)
- # self.out_text.bind("", self.select_all)
else:
self.out_text.bind("", self.select_all)
-
+ #########################################################
+ # Progress Frame #
+ #########################################################
+ #Create the elements of the progress frame
progress_frame = ttk.Frame(overall_frame, padding='0.1i')
- self.progress_label = ttk.Label(
- progress_frame, text="Progress:", justify=tk.LEFT)
- self.progress_label.grid(row=0, column=0, sticky=tk.E, padx=5)
+ self.progress_label = ttk.Label(progress_frame, text="Progress:", justify=tk.LEFT)
self.progress_var = tk.DoubleVar()
- self.progress = ttk.Progressbar(
- progress_frame, orient="horizontal", variable=self.progress_var, length=200, maximum=100)
+ self.progress = ttk.Progressbar(progress_frame, orient="horizontal", variable=self.progress_var, length=200, maximum=100)
+ #Define layout of the frame
+ self.progress_label.grid(row=0, column=0, sticky=tk.E, padx=5)
self.progress.grid(row=0, column=1, sticky=tk.E)
- progress_frame.grid(row=5, column=0, sticky=tk.E)
-
- overall_frame.grid(row=0, column=1, sticky=tk.NSEW)
-
- overall_frame.columnconfigure(0, weight=1)
- toplevel_frame.columnconfigure(0, weight=1)
- toplevel_frame.columnconfigure(1, weight=4)
- # self.pack(fill=tk.BOTH, expand=1)
- # toplevel_frame.columnconfigure(0, weight=1)
- # toplevel_frame.rowconfigure(0, weight=1)
-
- toplevel_frame.grid(row=0, column=0, sticky=tk.NSEW)
-
+ progress_frame.grid(row=4, column=0, columnspan = 2, sticky=tk.SE)
+ #########################################################
+ # Tool Selection #
+ #########################################################
# Select the appropriate tool, if specified, otherwise the first tool
- self.tools_listbox.select_set(selected_item)
- self.tools_listbox.event_generate("<>")
- self.tools_listbox.see(selected_item)
-
+ self.tool_tree.focus(self.tool_name)
+ self.tool_tree.selection_set(self.tool_name)
+ self.tool_tree.event_generate("<>")
+ #########################################################
+ # Menus #
+ #########################################################
menubar = tk.Menu(self)
+
filemenu = tk.Menu(menubar, tearoff=0)
- filemenu.add_command(label="Set Working Directory",
- command=self.set_directory)
- filemenu.add_command(
- label="Locate WhiteboxTools exe", command=self.select_exe)
+ filemenu.add_command(label="Set Working Directory", command=self.set_directory)
+ filemenu.add_command(label="Locate WhiteboxTools exe", command=self.select_exe)
filemenu.add_command(label="Refresh Tools", command=self.refresh_tools)
filemenu.add_separator()
filemenu.add_command(label="Exit", command=self.quit)
menubar.add_cascade(label="File", menu=filemenu)
editmenu = tk.Menu(menubar, tearoff=0)
- editmenu.add_command(
- label="Cut", command=lambda: self.focus_get().event_generate("<>"))
- editmenu.add_command(
- label="Copy", command=lambda: self.focus_get().event_generate("<>"))
- editmenu.add_command(
- label="Paste", command=lambda: self.focus_get().event_generate("<>"))
-
+ editmenu.add_command(label="Cut", command=lambda: self.focus_get().event_generate("<>"))
+ editmenu.add_command(label="Copy", command=lambda: self.focus_get().event_generate("<>"))
+ editmenu.add_command(label="Paste", command=lambda: self.focus_get().event_generate("<>"))
menubar.add_cascade(label="Edit ", menu=editmenu)
helpmenu = tk.Menu(menubar, tearoff=0)
- helpmenu.add_command(
- label="About", command=self.help)
+ helpmenu.add_command(label="About", command=self.help)
+ helpmenu.add_command(label="License", command=self.license)
+ menubar.add_cascade(label="Help ", menu=helpmenu)
- helpmenu.add_command(
- label="License", command=self.license)
+ self.master.config(menu=menubar)
- menubar.add_cascade(label="Help ", menu=helpmenu)
+ #########################################################
+ # Functions (added/edited by Rachel) #
+ #########################################################
+ def get_toolboxes(self):
+ toolboxes = set()
+ for item in wbt.toolbox().splitlines(): # run wbt.toolbox with no tool specified--returns all
+ if item:
+ tb = item.split(":")[1].strip()
+ toolboxes.add(tb)
+ return sorted(toolboxes)
+
+ def sort_toolboxes(self):
+ self.upper_toolboxes = []
+ self.lower_toolboxes = []
+ for toolbox in self.toolbox_list:
+ if toolbox.find('/') == (-1): #Does not contain a subtoolbox, i.e. does not contain '/'
+ self.upper_toolboxes.append(toolbox) #add to both upper toolbox list and lower toolbox list
+ self.lower_toolboxes.append(toolbox)
+ else: #Contains a subtoolbox
+ self.lower_toolboxes.append(toolbox) #add to only the lower toolbox list
+ self.upper_toolboxes = sorted(self.upper_toolboxes) #sort both lists alphabetically
+ self.lower_toolboxes = sorted(self.lower_toolboxes)
+
+ def sort_tools_by_toolbox(self):
+ self.sorted_tools = [[] for i in range(len(self.lower_toolboxes))] #One list for each lower toolbox
+ count = 1
+ for toolAndToolbox in self.tools_and_toolboxes.split('\n'):
+ if toolAndToolbox.strip():
+ tool = toolAndToolbox.strip().split(':')[0].strip().replace("TIN", "Tin").replace("KS", "Ks").replace("FD", "Fd") #current tool
+ itemToolbox = toolAndToolbox.strip().split(':')[1].strip() #current toolbox
+ index = 0
+ for toolbox in self.lower_toolboxes: #find which toolbox the current tool belongs to
+ if toolbox == itemToolbox:
+ self.sorted_tools[index].append(tool) #add current tool to list at appropriate index
+ break
+ index = index + 1
+ count = count + 1
- self.master.config(menu=menubar)
+ def get_tools_list(self):
+ self.tools_list = []
+ selected_item = -1
+ for item in wbt.list_tools().keys():
+ if item:
+ value = to_camelcase(item).replace("TIN", "Tin").replace("KS", "Ks").replace("FD", "Fd") #format tool name
+ self.tools_list.append(value) #add tool to list
+ if item == self.tool_name: #update selected_item it tool found
+ selected_item = len(self.tools_list) - 1
+ if selected_item == -1: #set self.tool_name as default tool
+ selected_item = 0
+ self.tool_name = self.tools_list[0]
+
+ def tree_update_tool_help(self, event): # read selection when tool selected from treeview then call self.update_tool_help
+ curItem = self.tool_tree.focus()
+ self.tool_name = self.tool_tree.item(curItem).get('text').replace(" ", "")
+ self.update_tool_help()
+
+ def search_update_tool_help(self, event): # read selection when tool selected from search results then call self.update_tool_help
+ selection = self.search_results_listbox.curselection()
+ self.tool_name = self.search_results_listbox.get(selection[0])
+ self.update_tool_help()
+
+ def update_tool_help(self):
+ self.out_text.delete('1.0', tk.END)
+ for widget in self.arg_scroll_frame.winfo_children():
+ widget.destroy()
- # self.get_toolboxes()
+ k = wbt.tool_help(self.tool_name)
+ self.print_to_output(k)
+ j = json.loads(wbt.tool_parameters(self.tool_name))
+ param_num = 0
+ for p in j['parameters']:
+ json_str = json.dumps(
+ p, sort_keys=True, indent=2, separators=(',', ': '))
+ pt = p['parameter_type']
+ if 'ExistingFileOrFloat' in pt:
+ ff = FileOrFloat(json_str, self, self.arg_scroll_frame)
+ ff.grid(row=param_num, column=0, sticky=tk.NSEW)
+ param_num = param_num + 1
+ elif ('ExistingFile' in pt or 'NewFile' in pt or 'Directory' in pt):
+ fs = FileSelector(json_str, self, self.arg_scroll_frame)
+ fs.grid(row=param_num, column=0, sticky=tk.NSEW)
+ param_num = param_num + 1
+ elif 'FileList' in pt:
+ b = MultifileSelector(json_str, self, self.arg_scroll_frame)
+ b.grid(row=param_num, column=0, sticky=tk.W)
+ param_num = param_num + 1
+ elif 'Boolean' in pt:
+ b = BooleanInput(json_str, self.arg_scroll_frame)
+ b.grid(row=param_num, column=0, sticky=tk.W)
+ param_num = param_num + 1
+ elif 'OptionList' in pt:
+ b = OptionsInput(json_str, self.arg_scroll_frame)
+ b.grid(row=param_num, column=0, sticky=tk.W)
+ param_num = param_num + 1
+ elif ('Float' in pt or 'Integer' in pt or
+ 'String' in pt or 'StringOrNumber' in pt or
+ 'StringList' in pt or 'VectorAttributeField' in pt):
+ b = DataInput(json_str, self.arg_scroll_frame)
+ b.grid(row=param_num, column=0, sticky=tk.NSEW)
+ param_num = param_num + 1
+ else:
+ messagebox.showinfo(
+ "Error", "Unsupported parameter type: {}.".format(pt))
+ self.update_args_box()
+ self.out_text.see("%d.%d" % (1, 0))
+
+ def update_toolbox_icon(self, event):
+ curItem = self.tool_tree.focus()
+ dict = self.tool_tree.item(curItem) #retreive the toolbox name
+ self.toolbox_name = dict.get('text'). replace(" ", "") #delete the space between the icon and text
+ self.toolbox_open = dict.get('open') #retreive whether the toolbox is open or not
+ if self.toolbox_open == True: #set image accordingly
+ self.tool_tree.item(self.toolbox_name, image = self.open_toolbox_icon)
+ else:
+ self.tool_tree.item(self.toolbox_name, image = self.closed_toolbox_icon)
+
+ def update_search(self, event):
+ self.search_list = []
+ self.search_string = self.search_text.get().lower()
+ self.search_results_listbox.delete(0, 'end') #empty the search results
+ num_results = 0
+ for tool in self.tools_list: #search tool names
+ toolLower = tool.lower()
+ if toolLower.find(self.search_string) != (-1): #search string found within tool name
+ num_results = num_results + 1
+ self.search_results_listbox.insert(num_results, tool) #tool added to listbox and to search results string
+ self.search_list.append(tool)
+ index = 0
+ self.get_descriptions()
+ for description in self.descriptionList: #search tool descriptions
+ descriptionLower = description.lower()
+ if descriptionLower.find(self.search_string) != (-1): #search string found within tool description
+ found = 0
+ for item in self.search_list: # check if this tool is already in the listbox
+ if self.tools_list[index] == item:
+ found = 1
+ if found == 0: # add to listbox
+ num_results = num_results + 1
+ self.search_results_listbox.insert(num_results, self.tools_list[index]) #tool added to listbox and to search results string
+ index = index + 1
+ self.search_frame['text'] = "{} Tools Found".format(num_results) #update search label
+
+ def get_descriptions(self):
+ self.descriptionList = []
+ tools = wbt.list_tools()
+ toolsItems = tools.items()
+ for t in toolsItems:
+ self.descriptionList.append(t[1]) #second entry in tool dictionary is the description
+
+ def configure_arg_scroll_frame(self, event):
+ # update the scrollbars to match the size of the inner frame
+ size = (self.arg_scroll_frame.winfo_reqwidth(), self.arg_scroll_frame.winfo_reqheight())
+ self.arg_canvas.config(scrollregion="0 0 %s %s" % size)
+ if self.arg_scroll_frame.winfo_reqwidth() != self.arg_canvas.winfo_width():
+ # update the canvas's width to fit the inner frame
+ self.arg_canvas.config(width=self.arg_scroll_frame.winfo_reqwidth())
+
+ def configure_arg_canvas(self, event):
+ if self.arg_scroll_frame.winfo_reqwidth() != self.arg_canvas.winfo_width():
+ # update the inner frame's width to fill the canvas
+ self.arg_canvas.itemconfigure(self.arg_scroll_frame_id, width=self.arg_canvas.winfo_width())
+
+ def tool_help_button(self):
+ index = 0
+ found = False
+ #find toolbox corresponding to the current tool
+ for toolbox in self.lower_toolboxes:
+ for tool in self.sorted_tools[index]:
+ if tool == self.tool_name:
+ self.toolbox_name = toolbox
+ found = True
+ break
+ if found:
+ break
+ index = index + 1
+ #change LiDAR to Lidar
+ if index == 10:
+ self.toolbox_name = to_camelcase(self.toolbox_name)
+ #format subtoolboxes as for URLs
+ self.toolbox_name = self.camel_to_snake(self.toolbox_name).replace('/', '').replace(' ', '')
+ #open the user manual section for the current tool
+ webbrowser.open_new_tab("https://jblindsay.github.io/wbt_book/available_tools/" + self.toolbox_name + ".html#" + self.tool_name)
+
+ def camel_to_snake(self, s): # taken from tools_info.py
+ _underscorer1 = re.compile(r'(.)([A-Z][a-z]+)')
+ _underscorer2 = re.compile('([a-z0-9])([A-Z])')
+ subbed = _underscorer1.sub(r'\1_\2', s)
+ return _underscorer2.sub(r'\1_\2', subbed).lower()
+
+ def refresh_tools(self):
+ #refresh lists
+ self.tools_and_toolboxes = wbt.toolbox('')
+ self.sort_tools_by_toolbox()
+ self.get_tools_list()
+ #clear self.tool_tree
+ self.tool_tree.delete(*self.tool_tree.get_children())
+ #Add toolboxes and tools to treeview
+ index = 0
+ for toolbox in self.lower_toolboxes:
+ if toolbox.find('/') != (-1): #toolboxes
+ self.tool_tree.insert(toolbox[:toolbox.find('/')], 0, text = " " + toolbox[toolbox.find('/') + 1:], iid = toolbox[toolbox.find('/') + 1:], tags = 'toolbox', image = self.closed_toolbox_icon)
+ for tool in self.sorted_tools[index]: #add tools within toolbox
+ self.tool_tree.insert(toolbox[toolbox.find('/') + 1:], 'end', text = " " + tool, tags = 'tool', iid = tool, image = self.tool_icon)
+ else: #subtoolboxes
+ self.tool_tree.insert('', 'end', text = " " + toolbox, iid = toolbox, tags = 'toolbox', image = self.closed_toolbox_icon)
+ for tool in self.sorted_tools[index]: #add tools within subtoolbox
+ self.tool_tree.insert(toolbox, 'end', text = " " + tool, iid = tool, tags = 'tool', image = self.tool_icon)
+ index = index + 1
+ #Update label
+ self.tools_frame["text"] = "{} Available Tools".format(len(self.tools_list))
+
+ #########################################################
+ # Functions (original) #
+ #########################################################
def help(self):
self.print_to_output(wbt.version())
@@ -956,7 +1216,7 @@ def run_tool(self):
# args = shlex.split(self.args_value.get())
args = []
- for widget in self.tool_args_frame.winfo_children():
+ for widget in self.arg_scroll_frame.winfo_children():
v = widget.get_value()
if v:
args.append(v)
@@ -993,58 +1253,7 @@ def cancel_operation(self):
def view_code(self):
webbrowser.open_new_tab(wbt.view_code(self.tool_name).strip())
-
- def update_tool_help(self, event):
- selection = self.tools_listbox.curselection()
- if(len(selection) == 0):
- return
- self.tool_name = self.tools_listbox.get(selection[0])
- self.out_text.delete('1.0', tk.END)
- for widget in self.tool_args_frame.winfo_children():
- widget.destroy()
-
- k = wbt.tool_help(self.tool_name)
- self.print_to_output(k)
-
- j = json.loads(wbt.tool_parameters(self.tool_name))
- param_num = 0
- for p in j['parameters']:
- json_str = json.dumps(
- p, sort_keys=True, indent=2, separators=(',', ': '))
- pt = p['parameter_type']
- if 'ExistingFileOrFloat' in pt:
- ff = FileOrFloat(json_str, self, self.tool_args_frame)
- ff.grid(row=param_num, column=0, sticky=tk.NSEW)
- param_num = param_num + 1
- elif ('ExistingFile' in pt or 'NewFile' in pt or 'Directory' in pt):
- fs = FileSelector(json_str, self, self.tool_args_frame)
- fs.grid(row=param_num, column=0, sticky=tk.NSEW)
- param_num = param_num + 1
- elif 'FileList' in pt:
- b = MultifileSelector(json_str, self, self.tool_args_frame)
- b.grid(row=param_num, column=0, sticky=tk.W)
- param_num = param_num + 1
- elif 'Boolean' in pt:
- b = BooleanInput(json_str, self.tool_args_frame)
- b.grid(row=param_num, column=0, sticky=tk.W)
- param_num = param_num + 1
- elif 'OptionList' in pt:
- b = OptionsInput(json_str, self.tool_args_frame)
- b.grid(row=param_num, column=0, sticky=tk.W)
- param_num = param_num + 1
- elif ('Float' in pt or 'Integer' in pt or
- 'String' in pt or 'StringOrNumber' in pt or
- 'StringList' in pt or 'VectorAttributeField' in pt):
- b = DataInput(json_str, self.tool_args_frame)
- b.grid(row=param_num, column=0, sticky=tk.NSEW)
- param_num = param_num + 1
- else:
- messagebox.showinfo(
- "Error", "Unsupported parameter type: {}.".format(pt))
-
- self.update_args_box()
- self.out_text.see("%d.%d" % (1, 0))
-
+
def update_args_box(self):
s = ""
self.current_tool_lbl['text'] = "Current Tool: {}".format(
@@ -1068,13 +1277,6 @@ def update_args_box(self):
# self.args_value.set(s.strip())
- def clear_args_box(self):
- self.args_value.set("")
-
- def args_select_all(self, event):
- self.args_text.select_range(0, tk.END)
- return 'break'
-
def custom_callback(self, value):
''' A custom callback for dealing with tool output.
'''
@@ -1102,42 +1304,6 @@ def select_all(self, event):
self.out_text.see(tk.INSERT)
return 'break'
- def get_tools_list(self):
- list = []
- selected_item = -1
- for item in wbt.list_tools().keys():
- if item:
- value = to_camelcase(item)
- list.append(value)
- if value == self.tool_name:
- selected_item = len(list) - 1
- if selected_item == -1:
- selected_item = 0
- self.tool_name = list[0]
-
- return (list, selected_item)
-
- def get_toolboxes(self):
- toolboxes = set()
- for item in wbt.toolbox().splitlines(): # run wbt.toolbox with no tool specified--returns all
- if item:
- tb = item.split(":")[1].strip()
- toolboxes.add(tb)
-
- for v in sorted(toolboxes):
- # print(v)
- self.print_line_to_output(v)
-
- def refresh_tools(self):
- (self.toolslist, selected_item) = self.get_tools_list()
- self.tools_listbox.delete(0, len(self.toolslist))
- for item in sorted(self.toolslist):
- self.tools_listbox.insert(len(self.toolslist), item)
-
- self.tools_frame["text"] = "{} Available Tools".format(
- len(self.toolslist))
-
-
class JsonPayload(object):
def __init__(self, j):
self.__dict__ = json.loads(j)
@@ -1147,10 +1313,9 @@ def main():
tool_name = None
if len(sys.argv) > 1:
tool_name = str(sys.argv[1])
-
wbr = WbRunner(tool_name)
wbr.mainloop()
if __name__ == '__main__':
- main()
+ main()
\ No newline at end of file
diff --git a/wb_runner_new.py b/wb_runner_new.py
deleted file mode 100644
index 1042c874e..000000000
--- a/wb_runner_new.py
+++ /dev/null
@@ -1,1321 +0,0 @@
-#!/usr/bin/env python3
-
-# This script is part of the WhiteboxTools geospatial analysis library.
-# Authors: Dr. John Lindsay
-# Created: November 28, 2017
-# Last Modified: November 28, 2017
-# License: MIT
-
-import __future__
-import sys
-# if sys.version_info[0] < 3:
-# raise Exception("Must be using Python 3")
-import json
-import os
-from os import path
-# from __future__ import print_function
-# from enum import Enum
-import platform
-import re #Added by Rachel for snake_to_camel function
-from pathlib import Path
-import glob
-from sys import platform as _platform
-import shlex
-import tkinter as tk
-from tkinter import ttk
-from tkinter.scrolledtext import ScrolledText
-from tkinter import filedialog
-from tkinter import messagebox
-from tkinter import PhotoImage
-import webbrowser
-from whitebox_tools import WhiteboxTools, to_camelcase
-
-wbt = WhiteboxTools()
-
-
-class FileSelector(tk.Frame):
- def __init__(self, json_str, runner, master=None):
- # first make sure that the json data has the correct fields
- j = json.loads(json_str)
- self.name = j['name']
- self.description = j['description']
- self.flag = j['flags'][len(j['flags']) - 1]
- self.parameter_type = j['parameter_type']
- self.file_type = ""
- if "ExistingFile" in self.parameter_type:
- self.file_type = j['parameter_type']['ExistingFile']
- elif "NewFile" in self.parameter_type:
- self.file_type = j['parameter_type']['NewFile']
- self.optional = j['optional']
- default_value = j['default_value']
-
- self.runner = runner
-
- ttk.Frame.__init__(self, master, padding='0.1i')
- self.grid()
-
- self.label = ttk.Label(self, text=self.name, justify=tk.LEFT)
- self.label.grid(row=0, column=0, sticky=tk.W)
- self.label.columnconfigure(0, weight=1)
-
- if not self.optional:
- self.label['text'] = self.label['text'] + "*"
-
- fs_frame = ttk.Frame(self, padding='0.0i')
- self.value = tk.StringVar()
- self.entry = ttk.Entry(
- fs_frame, width=45, justify=tk.LEFT, textvariable=self.value)
- self.entry.grid(row=0, column=0, sticky=tk.NSEW)
- self.entry.columnconfigure(0, weight=1)
- if default_value:
- self.value.set(default_value)
-
- # dir_path = os.path.dirname(os.path.realpath(__file__))
- # print(dir_path)
- # self.open_file_icon = tk.PhotoImage(file = dir_path + '//img//open.png') #Added by Rachel to replace file selector "..." button with open file icon
-
- # self.open_button = ttk.Button(fs_frame, width=4, image = self.open_file_icon, command=self.select_file, padding = '0.02i')
- self.open_button = ttk.Button(fs_frame, width=4, text="...", command=self.select_file, padding = '0.02i')
- self.open_button.grid(row=0, column=1, sticky=tk.E)
- self.open_button.columnconfigure(0, weight=1)
- fs_frame.grid(row=1, column=0, sticky=tk.NSEW)
- fs_frame.columnconfigure(0, weight=10)
- fs_frame.columnconfigure(1, weight=1)
- # self.pack(fill=tk.BOTH, expand=1)
- self.columnconfigure(0, weight=1)
- self.rowconfigure(0, weight=1)
- self.rowconfigure(1, weight=1)
-
- # Add the bindings
- if _platform == "darwin":
- self.entry.bind("", self.select_all)
- else:
- self.entry.bind("", self.select_all)
-
- def select_file(self):
- try:
- result = self.value.get()
- if self.parameter_type == "Directory":
- result = filedialog.askdirectory()
- elif "ExistingFile" in self.parameter_type:
- ftypes = [('All files', '*.*')]
- if 'RasterAndVector' in self.file_type:
- ftypes = [("Shapefiles", "*.shp"), ('Raster files', ('*.dep', '*.tif',
- '*.tiff', '*.flt',
- '*.sdat', '*.rdc',
- '*.asc'))]
- elif 'Raster' in self.file_type:
- ftypes = [('Raster files', ('*.dep', '*.tif',
- '*.tiff', '*.flt',
- '*.sdat', '*.rdc',
- '*.asc'))]
- elif 'Lidar' in self.file_type:
- ftypes = [("LiDAR files", ('*.las', '*.zip'))]
- elif 'Vector' in self.file_type:
- ftypes = [("Shapefiles", "*.shp")]
- elif 'Text' in self.file_type:
- ftypes = [("Text files", "*.txt"), ("all files", "*.*")]
- elif 'Csv' in self.file_type:
- ftypes = [("CSC files", "*.csv"), ("all files", "*.*")]
- elif 'Html' in self.file_type:
- ftypes = [("HTML files", "*.html")]
-
- result = filedialog.askopenfilename(
- initialdir=self.runner.working_dir, title="Select file", filetypes=ftypes)
-
- elif "NewFile" in self.parameter_type:
- result = filedialog.asksaveasfilename()
-
- self.value.set(result)
- # update the working directory
- self.runner.working_dir = os.path.dirname(result)
-
- except:
- t = "file"
- if self.parameter_type == "Directory":
- t = "directory"
- messagebox.showinfo("Warning", "Could not find {}".format(t))
-
- def get_value(self):
- if self.value.get():
- v = self.value.get()
- # Do some quality assurance here.
- # Is there a directory included?
- if not path.dirname(v):
- v = path.join(self.runner.working_dir, v)
-
- # What about a file extension?
- ext = os.path.splitext(v)[-1].lower().strip()
- if not ext:
- ext = ""
- if 'RasterAndVector' in self.file_type:
- ext = '.tif'
- elif 'Raster' in self.file_type:
- ext = '.tif'
- elif 'Lidar' in self.file_type:
- ext = '.las'
- elif 'Vector' in self.file_type:
- ext = '.shp'
- elif 'Text' in self.file_type:
- ext = '.txt'
- elif 'Csv' in self.file_type:
- ext = '.csv'
- elif 'Html' in self.file_type:
- ext = '.html'
-
- v += ext
-
- v = path.normpath(v)
-
- return "{}='{}'".format(self.flag, v)
- else:
- t = "file"
- if self.parameter_type == "Directory":
- t = "directory"
- if not self.optional:
- messagebox.showinfo(
- "Error", "Unspecified {} parameter {}.".format(t, self.flag))
-
- return None
-
- def select_all(self, event):
- self.entry.select_range(0, tk.END)
- return 'break'
-
-
-class FileOrFloat(tk.Frame):
- def __init__(self, json_str, runner, master=None):
- # first make sure that the json data has the correct fields
- j = json.loads(json_str)
- self.name = j['name']
- self.description = j['description']
- self.flag = j['flags'][len(j['flags']) - 1]
- self.parameter_type = j['parameter_type']
- self.file_type = j['parameter_type']['ExistingFileOrFloat']
- self.optional = j['optional']
- default_value = j['default_value']
-
- self.runner = runner
-
- ttk.Frame.__init__(self, master)
- self.grid()
- self['padding'] = '0.1i'
-
- self.label = ttk.Label(self, text=self.name, justify=tk.LEFT)
- self.label.grid(row=0, column=0, sticky=tk.W)
- self.label.columnconfigure(0, weight=1)
-
- if not self.optional:
- self.label['text'] = self.label['text'] + "*"
-
- fs_frame = ttk.Frame(self, padding='0.0i')
- self.value = tk.StringVar()
- self.entry = ttk.Entry(
- fs_frame, width=35, justify=tk.LEFT, textvariable=self.value)
- self.entry.grid(row=0, column=0, sticky=tk.NSEW)
- self.entry.columnconfigure(0, weight=1)
- if default_value:
- self.value.set(default_value)
-
- # self.img = tk.PhotoImage(file=script_dir + "/img/open.gif")
- # self.open_button = ttk.Button(fs_frame, width=55, image=self.img, command=self.select_dir)
- self.open_button = ttk.Button(
- fs_frame, width=4, text="...", command=self.select_file)
- self.open_button.grid(row=0, column=1, sticky=tk.E)
- # self.open_button.columnconfigure(0, weight=1)
-
- self.label = ttk.Label(fs_frame, text='OR', justify=tk.LEFT)
- self.label.grid(row=0, column=2, sticky=tk.W)
- # self.label.columnconfigure(0, weight=1)
-
- self.value2 = tk.StringVar()
- self.entry2 = ttk.Entry(
- fs_frame, width=10, justify=tk.LEFT, textvariable=self.value2)
- self.entry2.grid(row=0, column=3, sticky=tk.NSEW)
- self.entry2.columnconfigure(0, weight=1)
- self.entry2['justify'] = 'right'
-
- fs_frame.grid(row=1, column=0, sticky=tk.NSEW)
- fs_frame.columnconfigure(0, weight=10)
- fs_frame.columnconfigure(1, weight=1)
- # self.pack(fill=tk.BOTH, expand=1)
- self.columnconfigure(0, weight=1)
- self.rowconfigure(0, weight=1)
- self.rowconfigure(1, weight=1)
-
- # Add the bindings
- if _platform == "darwin":
- self.entry.bind("", self.select_all)
- else:
- self.entry.bind("", self.select_all)
-
- def select_file(self):
- try:
- result = self.value.get()
- ftypes = [('All files', '*.*')]
- if 'RasterAndVector' in self.file_type:
- ftypes = [("Shapefiles", "*.shp"), ('Raster files', ('*.dep', '*.tif',
- '*.tiff', '*.flt',
- '*.sdat', '*.rdc',
- '*.asc'))]
- elif 'Raster' in self.file_type:
- ftypes = [('Raster files', ('*.dep', '*.tif',
- '*.tiff', '*.flt',
- '*.sdat', '*.rdc',
- '*.asc'))]
- elif 'Lidar' in self.file_type:
- ftypes = [("LiDAR files", ('*.las', '*.zip'))]
- elif 'Vector' in self.file_type:
- ftypes = [("Shapefiles", "*.shp")]
- elif 'Text' in self.file_type:
- ftypes = [("Text files", "*.txt"), ("all files", "*.*")]
- elif 'Csv' in self.file_type:
- ftypes = [("CSC files", "*.csv"), ("all files", "*.*")]
- elif 'Html' in self.file_type:
- ftypes = [("HTML files", "*.html")]
-
- result = filedialog.askopenfilename(
- initialdir=self.runner.working_dir, title="Select file", filetypes=ftypes)
-
- self.value.set(result)
- # update the working directory
- self.runner.working_dir = os.path.dirname(result)
-
- except:
- t = "file"
- if self.parameter_type == "Directory":
- t = "directory"
- messagebox.showinfo("Warning", "Could not find {}".format(t))
-
- def RepresentsFloat(self, s):
- try:
- float(s)
- return True
- except ValueError:
- return False
-
- def get_value(self):
- if self.value.get():
- v = self.value.get()
- # Do some quality assurance here.
- # Is there a directory included?
- if not path.dirname(v):
- v = path.join(self.runner.working_dir, v)
-
- # What about a file extension?
- ext = os.path.splitext(v)[-1].lower()
- if not ext:
- ext = ""
- if 'RasterAndVector' in self.file_type:
- ext = '.tif'
- elif 'Raster' in self.file_type:
- ext = '.tif'
- elif 'Lidar' in self.file_type:
- ext = '.las'
- elif 'Vector' in self.file_type:
- ext = '.shp'
- elif 'Text' in self.file_type:
- ext = '.txt'
- elif 'Csv' in self.file_type:
- ext = '.csv'
- elif 'Html' in self.file_type:
- ext = '.html'
-
- v = v + ext
-
- v = path.normpath(v)
-
- return "{}='{}'".format(self.flag, v)
- elif self.value2.get():
- v = self.value2.get()
- if self.RepresentsFloat(v):
- return "{}={}".format(self.flag, v)
- else:
- messagebox.showinfo(
- "Error", "Error converting parameter {} to type Float.".format(self.flag))
- else:
- if not self.optional:
- messagebox.showinfo(
- "Error", "Unspecified file/numeric parameter {}.".format(self.flag))
-
- return None
-
- def select_all(self, event):
- self.entry.select_range(0, tk.END)
- return 'break'
-
-
-class MultifileSelector(tk.Frame):
- def __init__(self, json_str, runner, master=None):
- # first make sure that the json data has the correct fields
- j = json.loads(json_str)
- self.name = j['name']
- self.description = j['description']
- self.flag = j['flags'][len(j['flags']) - 1]
- self.parameter_type = j['parameter_type']
- self.file_type = ""
- self.file_type = j['parameter_type']['FileList']
- self.optional = j['optional']
- default_value = j['default_value']
-
- self.runner = runner
-
- ttk.Frame.__init__(self, master)
- self.grid()
- self['padding'] = '0.1i'
-
- self.label = ttk.Label(self, text=self.name, justify=tk.LEFT)
- self.label.grid(row=0, column=0, sticky=tk.W)
- self.label.columnconfigure(0, weight=1)
-
- if not self.optional:
- self.label['text'] = self.label['text'] + "*"
-
- fs_frame = ttk.Frame(self, padding='0.0i')
- # , variable=self.value)
- self.opt = tk.Listbox(fs_frame, width=44, height=4)
- self.opt.grid(row=0, column=0, sticky=tk.NSEW)
- s = ttk.Scrollbar(fs_frame, orient=tk.VERTICAL, command=self.opt.yview)
- s.grid(row=0, column=1, sticky=(tk.N, tk.S))
- self.opt['yscrollcommand'] = s.set
-
- btn_frame = ttk.Frame(fs_frame, padding='0.0i')
- self.open_button = ttk.Button(
- btn_frame, width=4, text="...", command=self.select_file)
- self.open_button.grid(row=0, column=0, sticky=tk.NE)
- self.open_button.columnconfigure(0, weight=1)
- self.open_button.rowconfigure(0, weight=1)
-
- self.delete_button = ttk.Button(
- btn_frame, width=4, text="del", command=self.delete_entry)
- self.delete_button.grid(row=1, column=0, sticky=tk.NE)
- self.delete_button.columnconfigure(0, weight=1)
- self.delete_button.rowconfigure(1, weight=1)
-
- btn_frame.grid(row=0, column=2, sticky=tk.NE)
-
- fs_frame.grid(row=1, column=0, sticky=tk.NSEW)
- fs_frame.columnconfigure(0, weight=10)
- fs_frame.columnconfigure(1, weight=1)
- fs_frame.columnconfigure(2, weight=1)
- # self.pack(fill=tk.BOTH, expand=1)
- self.columnconfigure(0, weight=1)
- self.rowconfigure(0, weight=1)
- self.rowconfigure(1, weight=1)
-
- def select_file(self):
- try:
- #result = self.value.get()
- init_dir = self.runner.working_dir
- ftypes = [('All files', '*.*')]
- if 'RasterAndVector' in self.file_type:
- ftypes = [("Shapefiles", "*.shp"), ('Raster files', ('*.dep', '*.tif',
- '*.tiff', '*.flt',
- '*.sdat', '*.rdc',
- '*.asc'))]
- elif 'Raster' in self.file_type:
- ftypes = [('Raster files', ('*.dep', '*.tif',
- '*.tiff', '*.flt',
- '*.sdat', '*.rdc',
- '*.asc'))]
- elif 'Lidar' in self.file_type:
- ftypes = [("LiDAR files", ('*.las', '*.zip'))]
- elif 'Vector' in self.file_type:
- ftypes = [("Shapefiles", "*.shp")]
- elif 'Text' in self.file_type:
- ftypes = [("Text files", "*.txt"), ("all files", "*.*")]
- elif 'Csv' in self.file_type:
- ftypes = [("CSC files", "*.csv"), ("all files", "*.*")]
- elif 'Html' in self.file_type:
- ftypes = [("HTML files", "*.html")]
-
- result = filedialog.askopenfilenames(
- initialdir=init_dir, title="Select files", filetypes=ftypes)
- if result:
- for v in result:
- self.opt.insert(tk.END, v)
-
- # update the working directory
- self.runner.working_dir = os.path.dirname(result[0])
-
- except:
- messagebox.showinfo("Warning", "Could not find file")
-
- def delete_entry(self):
- self.opt.delete(tk.ANCHOR)
-
- def get_value(self):
- try:
- l = self.opt.get(0, tk.END)
- if l:
- s = ""
- for i in range(0, len(l)):
- v = l[i]
- if not path.dirname(v):
- v = path.join(self.runner.working_dir, v)
- v = path.normpath(v)
- if i < len(l) - 1:
- s += "{};".format(v)
- else:
- s += "{}".format(v)
-
- return "{}='{}'".format(self.flag, s)
- else:
- if not self.optional:
- messagebox.showinfo(
- "Error", "Unspecified non-optional parameter {}.".format(self.flag))
-
- except:
- messagebox.showinfo(
- "Error", "Error formating files for parameter {}".format(self.flag))
-
- return None
-
-
-class BooleanInput(tk.Frame):
- def __init__(self, json_str, master=None):
- # first make sure that the json data has the correct fields
- j = json.loads(json_str)
- self.name = j['name']
- self.description = j['description']
- self.flag = j['flags'][len(j['flags']) - 1]
- self.parameter_type = j['parameter_type']
- # just for quality control. BooleanInputs are always optional.
- self.optional = True
- default_value = j['default_value']
-
- ttk.Frame.__init__(self, master)
- self.grid()
- self['padding'] = '0.1i'
-
- frame = ttk.Frame(self, padding='0.0i')
-
- self.value = tk.IntVar()
- c = ttk.Checkbutton(frame, text=self.name,
- width=55, variable=self.value)
- c.grid(row=0, column=0, sticky=tk.W)
-
- # set the default value
- if j['default_value'] != None and j['default_value'] != 'false':
- self.value.set(1)
- else:
- self.value.set(0)
-
- frame.grid(row=1, column=0, sticky=tk.W)
- frame.columnconfigure(0, weight=1)
-
- # self.pack(fill=tk.BOTH, expand=1)
- self.columnconfigure(0, weight=1)
- self.rowconfigure(0, weight=1)
-
- def get_value(self):
- if self.value.get() == 1:
- return self.flag
- else:
- return None
-
-
-class OptionsInput(tk.Frame):
- def __init__(self, json_str, master=None):
- # first make sure that the json data has the correct fields
- j = json.loads(json_str)
- self.name = j['name']
- self.description = j['description']
- self.flag = j['flags'][len(j['flags']) - 1]
- self.parameter_type = j['parameter_type']
- self.optional = j['optional']
- default_value = j['default_value']
-
- ttk.Frame.__init__(self, master)
- self.grid()
- self['padding'] = '0.1i'
-
- frame = ttk.Frame(self, padding='0.0i')
-
- self.label = ttk.Label(self, text=self.name, justify=tk.LEFT)
- self.label.grid(row=0, column=0, sticky=tk.W)
- self.label.columnconfigure(0, weight=1)
-
- frame2 = ttk.Frame(frame, padding='0.0i')
- opt = ttk.Combobox(frame2, width=40)
- opt.grid(row=0, column=0, sticky=tk.NSEW)
-
- self.value = None # initialize in event of no default and no selection
- i = 1
- default_index = -1
- list = j['parameter_type']['OptionList']
- values = ()
- for v in list:
- values += (v,)
- # opt.insert(tk.END, v)
- if v == default_value:
- default_index = i - 1
- i = i + 1
-
- opt['values'] = values
-
- # opt.bind("<>", self.select)
- opt.bind("<>", self.select)
- if default_index >= 0:
- opt.current(default_index)
- opt.event_generate("<>")
- # opt.see(default_index)
-
- frame2.grid(row=0, column=0, sticky=tk.W)
- frame.grid(row=1, column=0, sticky=tk.W)
- frame.columnconfigure(0, weight=1)
-
- # self.pack(fill=tk.BOTH, expand=1)
- self.columnconfigure(0, weight=1)
- self.rowconfigure(0, weight=1)
-
- # # first make sure that the json data has the correct fields
- # j = json.loads(json_str)
- # self.name = j['name']
- # self.description = j['description']
- # self.flag = j['flags'][len(j['flags']) - 1]
- # self.parameter_type = j['parameter_type']
- # self.optional = j['optional']
- # default_value = j['default_value']
-
- # ttk.Frame.__init__(self, master)
- # self.grid()
- # self['padding'] = '0.1i'
-
- # frame = ttk.Frame(self, padding='0.0i')
-
- # self.label = ttk.Label(self, text=self.name, justify=tk.LEFT)
- # self.label.grid(row=0, column=0, sticky=tk.W)
- # self.label.columnconfigure(0, weight=1)
-
- # frame2 = ttk.Frame(frame, padding='0.0i')
- # opt = tk.Listbox(frame2, width=40) # , variable=self.value)
- # opt.grid(row=0, column=0, sticky=tk.NSEW)
- # s = ttk.Scrollbar(frame2, orient=tk.VERTICAL, command=opt.yview)
- # s.grid(row=0, column=1, sticky=(tk.N, tk.S))
- # opt['yscrollcommand'] = s.set
-
- # self.value = None # initialize in event of no default and no selection
- # i = 1
- # default_index = -1
- # list = j['parameter_type']['OptionList']
- # for v in list:
- # #opt.insert(i, v)
- # opt.insert(tk.END, v)
- # if v == default_value:
- # default_index = i - 1
- # i = i + 1
-
- # if i - 1 < 4:
- # opt['height'] = i - 1
- # else:
- # opt['height'] = 3
-
- # opt.bind("<>", self.select)
- # if default_index >= 0:
- # opt.select_set(default_index)
- # opt.event_generate("<>")
- # opt.see(default_index)
-
- # frame2.grid(row=0, column=0, sticky=tk.W)
- # frame.grid(row=1, column=0, sticky=tk.W)
- # frame.columnconfigure(0, weight=1)
-
- # # self.pack(fill=tk.BOTH, expand=1)
- # self.columnconfigure(0, weight=1)
- # self.rowconfigure(0, weight=1)
-
- def get_value(self):
- if self.value:
- return "{}='{}'".format(self.flag, self.value)
- else:
- if not self.optional:
- messagebox.showinfo(
- "Error", "Unspecified non-optional parameter {}.".format(self.flag))
-
- return None
-
- def select(self, event):
- widget = event.widget
- # selection = widget.curselection()
- self.value = widget.get() # selection[0])
-
-
-class DataInput(tk.Frame):
- def __init__(self, json_str, master=None):
- # first make sure that the json data has the correct fields
- j = json.loads(json_str)
- self.name = j['name']
- self.description = j['description']
- self.flag = j['flags'][len(j['flags']) - 1]
- self.parameter_type = j['parameter_type']
- self.optional = j['optional']
- default_value = j['default_value']
-
- ttk.Frame.__init__(self, master)
- self.grid()
- self['padding'] = '0.1i'
-
- self.label = ttk.Label(self, text=self.name, justify=tk.LEFT)
- self.label.grid(row=0, column=0, sticky=tk.W)
- self.label.columnconfigure(0, weight=1)
-
- self.value = tk.StringVar()
- if default_value:
- self.value.set(default_value)
- else:
- self.value.set("")
-
- self.entry = ttk.Entry(self, justify=tk.LEFT, textvariable=self.value)
- self.entry.grid(row=0, column=1, sticky=tk.NSEW)
- self.entry.columnconfigure(1, weight=10)
-
- if not self.optional:
- self.label['text'] = self.label['text'] + "*"
-
- if ("Integer" in self.parameter_type or
- "Float" in self.parameter_type or
- "Double" in self.parameter_type):
- self.entry['justify'] = 'right'
-
- # Add the bindings
- if _platform == "darwin":
- self.entry.bind("", self.select_all)
- else:
- self.entry.bind("", self.select_all)
-
- # self.pack(fill=tk.BOTH, expand=1)
- self.columnconfigure(0, weight=1)
- self.columnconfigure(1, weight=10)
- self.rowconfigure(0, weight=1)
-
- def RepresentsInt(self, s):
- try:
- int(s)
- return True
- except ValueError:
- return False
-
- def RepresentsFloat(self, s):
- try:
- float(s)
- return True
- except ValueError:
- return False
-
- def get_value(self):
- v = self.value.get()
- if v:
- if "Integer" in self.parameter_type:
- if self.RepresentsInt(self.value.get()):
- return "{}={}".format(self.flag, self.value.get())
- else:
- messagebox.showinfo(
- "Error", "Error converting parameter {} to type Integer.".format(self.flag))
- elif "Float" in self.parameter_type:
- if self.RepresentsFloat(self.value.get()):
- return "{}={}".format(self.flag, self.value.get())
- else:
- messagebox.showinfo(
- "Error", "Error converting parameter {} to type Float.".format(self.flag))
- elif "Double" in self.parameter_type:
- if self.RepresentsFloat(self.value.get()):
- return "{}={}".format(self.flag, self.value.get())
- else:
- messagebox.showinfo(
- "Error", "Error converting parameter {} to type Double.".format(self.flag))
- else: # String or StringOrNumber types
- return "{}='{}'".format(self.flag, self.value.get())
- else:
- if not self.optional:
- messagebox.showinfo(
- "Error", "Unspecified non-optional parameter {}.".format(self.flag))
-
- return None
-
- def select_all(self, event):
- self.entry.select_range(0, tk.END)
- return 'break'
-
-
-class WbRunner(tk.Frame):
- def __init__(self, tool_name=None, master=None):
- if platform.system() == 'Windows':
- self.ext = '.exe'
- else:
- self.ext = ''
-
- exe_name = "whitebox_tools{}".format(self.ext)
-
- self.exe_path = path.dirname(path.abspath(__file__))
- os.chdir(self.exe_path)
- for filename in glob.iglob('**/*', recursive=True):
- if filename.endswith(exe_name):
- self.exe_path = path.dirname(path.abspath(filename))
- break
-
- wbt.set_whitebox_dir(self.exe_path)
-
- ttk.Frame.__init__(self, master)
- self.script_dir = os.path.dirname(os.path.realpath(__file__))
- self.grid()
- self.tool_name = tool_name
- self.master.title("WhiteboxTools Runner")
- if _platform == "darwin":
- os.system(
- '''/usr/bin/osascript -e 'tell app "Finder" to set frontmost of process "Python" to true' ''')
- self.create_widgets()
- self.working_dir = str(Path.home())
-
- def create_widgets(self):
-
- #########################################################
- # Overall/Top level Frame #
- #########################################################
- #define left-side frame (toplevel_frame) and right-side frame (overall_frame)
- toplevel_frame = ttk.Frame(self, padding='0.1i')
- overall_frame = ttk.Frame(self, padding='0.1i')
- #set-up layout
- overall_frame.grid(row=0, column=1, sticky=tk.NSEW)
- toplevel_frame.grid(row=0, column=0, sticky=tk.NSEW)
- #########################################################
- # Calling basics #
- #########################################################
- #Create all needed lists of tools and toolboxes
- self.toolbox_list = self.get_toolboxes()
- self.sort_toolboxes()
- self.tools_and_toolboxes = wbt.toolbox('')
- self.sort_tools_by_toolbox()
- self.get_tools_list()
- #Icons to be used in tool treeview
- self.tool_icon = tk.PhotoImage(file = self.script_dir + '//img//tool.png')
- self.open_toolbox_icon = tk.PhotoImage(file = self.script_dir + '//img//open.png')
- self.closed_toolbox_icon = tk.PhotoImage(file = self.script_dir + '//img//closed.png')
- #########################################################
- # Toolboxes Frame #FIXME: change width or make horizontally scrollable
- #########################################################
- #define tools_frame and tool_tree
- self.tools_frame = ttk.LabelFrame(toplevel_frame, text="{} Available Tools".format(len(self.tools_list)), padding='0.1i')
- self.tool_tree = ttk.Treeview(self.tools_frame, height = 21)
- #Set up layout
- self.tool_tree.grid(row=0, column=0, sticky=tk.NSEW)
- self.tool_tree.column("#0", width = 280) #Set width so all tools are readable within the frame
- self.tools_frame.grid(row=0, column=0, sticky=tk.NSEW)
- self.tools_frame.columnconfigure(0, weight=10)
- self.tools_frame.columnconfigure(1, weight=1)
- self.tools_frame.rowconfigure(0, weight=10)
- self.tools_frame.rowconfigure(1, weight=1)
- #Add toolboxes and tools to treeview
- index = 0
- for toolbox in self.lower_toolboxes:
- if toolbox.find('/') != (-1): #toolboxes
- self.tool_tree.insert(toolbox[:toolbox.find('/')], 0, text = " " + toolbox[toolbox.find('/') + 1:], iid = toolbox[toolbox.find('/') + 1:], tags = 'toolbox', image = self.closed_toolbox_icon)
- for tool in self.sorted_tools[index]: #add tools within toolbox
- self.tool_tree.insert(toolbox[toolbox.find('/') + 1:], 'end', text = " " + tool, tags = 'tool', iid = tool, image = self.tool_icon)
- else: #subtoolboxes
- self.tool_tree.insert('', 'end', text = " " + toolbox, iid = toolbox, tags = 'toolbox', image = self.closed_toolbox_icon)
- for tool in self.sorted_tools[index]: #add tools within subtoolbox
- self.tool_tree.insert(toolbox, 'end', text = " " + tool, iid = tool, tags = 'tool', image = self.tool_icon)
- index = index + 1
- #bind tools in treeview to self.tree_update_tool_help function and toolboxes to self.update_toolbox_icon function
- self.tool_tree.tag_bind('tool', "<>", self.tree_update_tool_help)
- self.tool_tree.tag_bind('toolbox', "<>", self.update_toolbox_icon)
- #Add vertical scrollbar to treeview frame
- s = ttk.Scrollbar(self.tools_frame, orient=tk.VERTICAL,command=self.tool_tree.yview)
- s.grid(row=0, column=1, sticky=(tk.N, tk.S))
- self.tool_tree['yscrollcommand'] = s.set
- #########################################################
- # Search Bar #
- #########################################################
- #create variables for search results and search input
- self.search_list = []
- self.search_text = tk.StringVar()
- #Create the elements of the search frame
- self.search_frame = ttk.LabelFrame(toplevel_frame, padding='0.1i', text="{} Tools Found".format(len(self.search_list)))
- self.search_label = ttk.Label(self.search_frame, text = "Search: ")
- self.search_bar = ttk.Entry(self.search_frame, width = 30, textvariable = self.search_text)
- self.search_results_listbox = tk.Listbox(self.search_frame, height=11)
- self.search_scroll = ttk.Scrollbar(self.search_frame, orient=tk.VERTICAL, command=self.search_results_listbox.yview)
- self.search_results_listbox['yscrollcommand'] = self.search_scroll.set
- #Add bindings
- self.search_results_listbox.bind("<>", self.search_update_tool_help)
- self.search_bar.bind('', self.update_search)
- #Define layout of the frame
- self.search_frame.grid(row = 1, column = 0, sticky=tk.NSEW)
- self.search_label.grid(row = 0, column = 0, sticky=tk.NW)
- self.search_bar.grid(row = 0, column = 1, sticky=tk.NE)
- self.search_results_listbox.grid(row = 1, column = 0, columnspan = 2, sticky=tk.NSEW, pady = 5)
- self.search_scroll.grid(row=1, column=2, sticky=(tk.N, tk.S))
- #Configure rows and columns of the frame
- self.search_frame.columnconfigure(0, weight=1)
- self.search_frame.columnconfigure(1, weight=10)
- self.search_frame.columnconfigure(1, weight=1)
- self.search_frame.rowconfigure(0, weight=1)
- self.search_frame.rowconfigure(1, weight = 10)
- #########################################################
- # Current Tool Frame #
- #########################################################
- #Create the elements of the current tool frame
- self.current_tool_frame = ttk.Frame(overall_frame, padding='0.2i')
- self.current_tool_lbl = ttk.Label(self.current_tool_frame, text="Current Tool: {}".format(self.tool_name), justify=tk.LEFT) # , font=("Helvetica", 12, "bold")
- self.view_code_button = ttk.Button(self.current_tool_frame, text="View Code", width=12, command=self.view_code)
- #Define layout of the frame
- self.view_code_button.grid(row=0, column=1, sticky=tk.E)
- self.current_tool_lbl.grid(row=0, column=0, sticky=tk.W)
- self.current_tool_frame.grid(row=0, column=0, columnspan = 2, sticky=tk.NSEW)
- #Configure rows and columns of the frame
- self.current_tool_frame.columnconfigure(0, weight=1)
- self.current_tool_frame.columnconfigure(1, weight=1)
- #########################################################
- # Args Frame #
- #########################################################
- # #Create the elements of the tool arguments frame
- # self.arg_scroll = ttk.Scrollbar(overall_frame, orient='vertical')
- # self.arg_canvas = tk.Canvas(overall_frame, bd=0, highlightthickness=0, yscrollcommand=self.arg_scroll.set)
- # self.arg_scroll.config(command=self.arg_canvas.yview) #self.arg_scroll scrolls over self.arg_canvas
- # self.arg_scroll_frame = ttk.Frame(self.arg_canvas) # create a frame inside the self.arg_canvas which will be scrolled with it
- # self.arg_scroll_frame_id = self.arg_canvas.create_window(0, 0, window=self.arg_scroll_frame, anchor="nw")
- # #Define layout of the frame
- # self.arg_scroll.grid(row = 1, column = 1, sticky = (tk.NS, tk.E))
- # self.arg_canvas.grid(row = 1, column = 0, sticky = tk.NSEW)
- # # reset the view
- # self.arg_canvas.xview_moveto(0)
- # self.arg_canvas.yview_moveto(0)
- # #Add bindings
- # self.arg_scroll_frame.bind('', self.configure_arg_scroll_frame)
- # self.arg_canvas.bind('', self.configure_arg_canvas)
- self.arg_scroll_frame = ttk.Frame(overall_frame, padding='0.0i')
- self.arg_scroll_frame.grid(row=2, column=0, sticky=tk.NSEW)
- self.arg_scroll_frame.columnconfigure(0, weight=1)
- #########################################################
- # Buttons Frame #
- #########################################################
- #Create the elements of the buttons frame
- self.buttons_frame = ttk.Frame(overall_frame, padding='0.2i')
- self.run_button = ttk.Button(self.buttons_frame, text="Run", width=8, command=self.run_tool)
- self.quit_button = ttk.Button(self.buttons_frame, text="Cancel", width=8, command=self.cancel_operation)
- self.help_button = ttk.Button(self.buttons_frame, text="Help", width=8, command=self.tool_help_button)
- #Define layout of the frame
- self.run_button.grid(row=0, column=0)
- self.quit_button.grid(row=0, column=1)
- self.help_button.grid(row = 0, column = 2)
- self.buttons_frame.grid(row=2, column=0, columnspan = 2, sticky=tk.E)
- #########################################################
- # Output Frame #
- #########################################################
- #Create the elements of the output frame
- output_frame = ttk.Frame(overall_frame)
- outlabel = ttk.Label(output_frame, text="Output:", justify=tk.LEFT)
- self.out_text = ScrolledText(output_frame, width=63, height=15, wrap=tk.NONE, padx=7, pady=7, exportselection = 0)
- output_scrollbar = ttk.Scrollbar(output_frame, orient=tk.HORIZONTAL, command = self.out_text.xview)
- self.out_text['xscrollcommand'] = output_scrollbar.set
- #Retreive and insert the text for the current tool
- k = wbt.tool_help(self.tool_name)
- self.out_text.insert(tk.END, k)
- #Define layout of the frame
- outlabel.grid(row=0, column=0, sticky=tk.NW)
- self.out_text.grid(row=1, column=0, sticky=tk.NSEW)
- output_frame.grid(row=3, column=0, columnspan = 2, sticky=(tk.NS, tk.E))
- output_scrollbar.grid(row=2, column=0, sticky=(tk.W, tk.E))
- #Configure rows and columns of the frame
- self.out_text.columnconfigure(0, weight=1)
- output_frame.columnconfigure(0, weight=1)
- # Add the binding
- if _platform == "darwin":
- self.out_text.bind("", self.select_all)
- else:
- self.out_text.bind("", self.select_all)
- #########################################################
- # Progress Frame #
- #########################################################
- #Create the elements of the progress frame
- progress_frame = ttk.Frame(overall_frame, padding='0.2i')
- self.progress_label = ttk.Label(progress_frame, text="Progress:", justify=tk.LEFT)
- self.progress_var = tk.DoubleVar()
- self.progress = ttk.Progressbar(progress_frame, orient="horizontal", variable=self.progress_var, length=200, maximum=100)
- #Define layout of the frame
- self.progress_label.grid(row=0, column=0, sticky=tk.E, padx=5)
- self.progress.grid(row=0, column=1, sticky=tk.E)
- progress_frame.grid(row=4, column=0, columnspan = 2, sticky=tk.SE)
- #########################################################
- # Tool Selection #
- #########################################################
- # Select the appropriate tool, if specified, otherwise the first tool
- self.tool_tree.focus(self.tool_name)
- self.tool_tree.selection_set(self.tool_name)
- self.tool_tree.event_generate("<>")
- #########################################################
- # Menus #
- #########################################################
- menubar = tk.Menu(self)
-
- filemenu = tk.Menu(menubar, tearoff=0)
- filemenu.add_command(label="Set Working Directory", command=self.set_directory)
- filemenu.add_command(label="Locate WhiteboxTools exe", command=self.select_exe)
- filemenu.add_command(label="Refresh Tools", command=self.refresh_tools)
- filemenu.add_separator()
- filemenu.add_command(label="Exit", command=self.quit)
- menubar.add_cascade(label="File", menu=filemenu)
-
- editmenu = tk.Menu(menubar, tearoff=0)
- editmenu.add_command(label="Cut", command=lambda: self.focus_get().event_generate("<>"))
- editmenu.add_command(label="Copy", command=lambda: self.focus_get().event_generate("<>"))
- editmenu.add_command(label="Paste", command=lambda: self.focus_get().event_generate("<>"))
- menubar.add_cascade(label="Edit ", menu=editmenu)
-
- helpmenu = tk.Menu(menubar, tearoff=0)
- helpmenu.add_command(label="About", command=self.help)
- helpmenu.add_command(label="License", command=self.license)
- menubar.add_cascade(label="Help ", menu=helpmenu)
-
- self.master.config(menu=menubar)
-
- #########################################################
- # Functions (added/edited by Rachel) #
- #########################################################
- def get_toolboxes(self):
- toolboxes = set()
- for item in wbt.toolbox().splitlines(): # run wbt.toolbox with no tool specified--returns all
- if item:
- tb = item.split(":")[1].strip()
- toolboxes.add(tb)
- return sorted(toolboxes)
-
- def sort_toolboxes(self):
- self.upper_toolboxes = []
- self.lower_toolboxes = []
- for toolbox in self.toolbox_list:
- if toolbox.find('/') == (-1): #Does not contain a subtoolbox, i.e. does not contain '/'
- self.upper_toolboxes.append(toolbox) #add to both upper toolbox list and lower toolbox list
- self.lower_toolboxes.append(toolbox)
- else: #Contains a subtoolbox
- self.lower_toolboxes.append(toolbox) #add to only the lower toolbox list
- self.upper_toolboxes = sorted(self.upper_toolboxes) #sort both lists alphabetically
- self.lower_toolboxes = sorted(self.lower_toolboxes)
-
- def sort_tools_by_toolbox(self):
- self.sorted_tools = [[] for i in range(len(self.lower_toolboxes))] #One list for each lower toolbox
- count = 1
- for toolAndToolbox in self.tools_and_toolboxes.split('\n'):
- if toolAndToolbox.strip():
- tool = toolAndToolbox.strip().split(':')[0].strip().replace("TIN", "Tin").replace("KS", "Ks").replace("FD", "Fd") #current tool
- itemToolbox = toolAndToolbox.strip().split(':')[1].strip() #current toolbox
- index = 0
- for toolbox in self.lower_toolboxes: #find which toolbox the current tool belongs to
- if toolbox == itemToolbox:
- self.sorted_tools[index].append(tool) #add current tool to list at appropriate index
- break
- index = index + 1
- count = count + 1
-
- def get_tools_list(self):
- self.tools_list = []
- selected_item = -1
- for item in wbt.list_tools().keys():
- if item:
- value = to_camelcase(item).replace("TIN", "Tin").replace("KS", "Ks").replace("FD", "Fd") #format tool name
- self.tools_list.append(value) #add tool to list
- if item == self.tool_name: #update selected_item it tool found
- selected_item = len(self.tools_list) - 1
- if selected_item == -1: #set self.tool_name as default tool
- selected_item = 0
- self.tool_name = self.tools_list[0]
-
- def tree_update_tool_help(self, event): # read selection when tool selected from treeview then call self.update_tool_help
- curItem = self.tool_tree.focus()
- self.tool_name = self.tool_tree.item(curItem).get('text').replace(" ", "")
- self.update_tool_help()
-
- def search_update_tool_help(self, event): # read selection when tool selected from search results then call self.update_tool_help
- selection = self.search_results_listbox.curselection()
- self.tool_name = self.search_results_listbox.get(selection[0])
- self.update_tool_help()
-
- def update_tool_help(self):
- self.out_text.delete('1.0', tk.END)
- for widget in self.arg_scroll_frame.winfo_children():
- widget.destroy()
-
- k = wbt.tool_help(self.tool_name)
- self.print_to_output(k)
-
- j = json.loads(wbt.tool_parameters(self.tool_name))
- param_num = 0
- for p in j['parameters']:
- json_str = json.dumps(
- p, sort_keys=True, indent=2, separators=(',', ': '))
- pt = p['parameter_type']
- if 'ExistingFileOrFloat' in pt:
- ff = FileOrFloat(json_str, self, self.arg_scroll_frame)
- ff.grid(row=param_num, column=0, sticky=tk.NSEW)
- param_num = param_num + 1
- elif ('ExistingFile' in pt or 'NewFile' in pt or 'Directory' in pt):
- fs = FileSelector(json_str, self, self.arg_scroll_frame)
- fs.grid(row=param_num, column=0, sticky=tk.NSEW)
- param_num = param_num + 1
- elif 'FileList' in pt:
- b = MultifileSelector(json_str, self, self.arg_scroll_frame)
- b.grid(row=param_num, column=0, sticky=tk.W)
- param_num = param_num + 1
- elif 'Boolean' in pt:
- b = BooleanInput(json_str, self.arg_scroll_frame)
- b.grid(row=param_num, column=0, sticky=tk.W)
- param_num = param_num + 1
- elif 'OptionList' in pt:
- b = OptionsInput(json_str, self.arg_scroll_frame)
- b.grid(row=param_num, column=0, sticky=tk.W)
- param_num = param_num + 1
- elif ('Float' in pt or 'Integer' in pt or
- 'String' in pt or 'StringOrNumber' in pt or
- 'StringList' in pt or 'VectorAttributeField' in pt):
- b = DataInput(json_str, self.arg_scroll_frame)
- b.grid(row=param_num, column=0, sticky=tk.NSEW)
- param_num = param_num + 1
- else:
- messagebox.showinfo(
- "Error", "Unsupported parameter type: {}.".format(pt))
- self.update_args_box()
- self.out_text.see("%d.%d" % (1, 0))
-
- def update_toolbox_icon(self, event):
- curItem = self.tool_tree.focus()
- dict = self.tool_tree.item(curItem) #retreive the toolbox name
- self.toolbox_name = dict.get('text'). replace(" ", "") #delete the space between the icon and text
- self.toolbox_open = dict.get('open') #retreive whether the toolbox is open or not
- if self.toolbox_open == True: #set image accordingly
- self.tool_tree.item(self.toolbox_name, image = self.open_toolbox_icon)
- else:
- self.tool_tree.item(self.toolbox_name, image = self.closed_toolbox_icon)
-
- def update_search(self, event):
- self.search_list = []
- self.search_string = self.search_text.get().lower()
- self.search_results_listbox.delete(0, 'end') #empty the search results
- num_results = 0
- for tool in self.tools_list: #search tool names
- toolLower = tool.lower()
- if toolLower.find(self.search_string) != (-1): #search string found within tool name
- num_results = num_results + 1
- self.search_results_listbox.insert(num_results, tool) #tool added to listbox and to search results string
- self.search_list.append(tool)
- index = 0
- self.get_descriptions()
- for description in self.descriptionList: #search tool descriptions
- descriptionLower = description.lower()
- if descriptionLower.find(self.search_string) != (-1): #search string found within tool description
- found = 0
- for item in self.search_list: # check if this tool is already in the listbox
- if self.tools_list[index] == item:
- found = 1
- if found == 0: # add to listbox
- num_results = num_results + 1
- self.search_results_listbox.insert(num_results, self.tools_list[index]) #tool added to listbox and to search results string
- index = index + 1
- self.search_frame['text'] = "{} Tools Found".format(num_results) #update search label
-
- def get_descriptions(self):
- self.descriptionList = []
- tools = wbt.list_tools()
- toolsItems = tools.items()
- for t in toolsItems:
- self.descriptionList.append(t[1]) #second entry in tool dictionary is the description
-
- def configure_arg_scroll_frame(self, event):
- # update the scrollbars to match the size of the inner frame
- size = (self.arg_scroll_frame.winfo_reqwidth(), self.arg_scroll_frame.winfo_reqheight())
- self.arg_canvas.config(scrollregion="0 0 %s %s" % size)
- if self.arg_scroll_frame.winfo_reqwidth() != self.arg_canvas.winfo_width():
- # update the canvas's width to fit the inner frame
- self.arg_canvas.config(width=self.arg_scroll_frame.winfo_reqwidth())
-
- def configure_arg_canvas(self, event):
- if self.arg_scroll_frame.winfo_reqwidth() != self.arg_canvas.winfo_width():
- # update the inner frame's width to fill the canvas
- self.arg_canvas.itemconfigure(self.arg_scroll_frame_id, width=self.arg_canvas.winfo_width())
-
- def tool_help_button(self):
- index = 0
- found = False
- #find toolbox corresponding to the current tool
- for toolbox in self.lower_toolboxes:
- for tool in self.sorted_tools[index]:
- if tool == self.tool_name:
- self.toolbox_name = toolbox
- found = True
- break
- if found:
- break
- index = index + 1
- #change LiDAR to Lidar
- if index == 10:
- self.toolbox_name = to_camelcase(self.toolbox_name)
- #format subtoolboxes as for URLs
- self.toolbox_name = self.camel_to_snake(self.toolbox_name).replace('/', '').replace(' ', '')
- #open the user manual section for the current tool
- webbrowser.open_new_tab("https://jblindsay.github.io/wbt_book/available_tools/" + self.toolbox_name + ".html#" + self.tool_name)
-
- def camel_to_snake(self, s): # taken from tools_info.py
- _underscorer1 = re.compile(r'(.)([A-Z][a-z]+)')
- _underscorer2 = re.compile('([a-z0-9])([A-Z])')
- subbed = _underscorer1.sub(r'\1_\2', s)
- return _underscorer2.sub(r'\1_\2', subbed).lower()
-
- def refresh_tools(self):
- #refresh lists
- self.tools_and_toolboxes = wbt.toolbox('')
- self.sort_tools_by_toolbox()
- self.get_tools_list()
- #clear self.tool_tree
- self.tool_tree.delete(*self.tool_tree.get_children())
- #Add toolboxes and tools to treeview
- index = 0
- for toolbox in self.lower_toolboxes:
- if toolbox.find('/') != (-1): #toolboxes
- self.tool_tree.insert(toolbox[:toolbox.find('/')], 0, text = " " + toolbox[toolbox.find('/') + 1:], iid = toolbox[toolbox.find('/') + 1:], tags = 'toolbox', image = self.closed_toolbox_icon)
- for tool in self.sorted_tools[index]: #add tools within toolbox
- self.tool_tree.insert(toolbox[toolbox.find('/') + 1:], 'end', text = " " + tool, tags = 'tool', iid = tool, image = self.tool_icon)
- else: #subtoolboxes
- self.tool_tree.insert('', 'end', text = " " + toolbox, iid = toolbox, tags = 'toolbox', image = self.closed_toolbox_icon)
- for tool in self.sorted_tools[index]: #add tools within subtoolbox
- self.tool_tree.insert(toolbox, 'end', text = " " + tool, iid = tool, tags = 'tool', image = self.tool_icon)
- index = index + 1
- #Update label
- self.tools_frame["text"] = "{} Available Tools".format(len(self.tools_list))
-
- #########################################################
- # Functions (original) #
- #########################################################
- def help(self):
- self.print_to_output(wbt.version())
-
- def license(self):
- self.print_to_output(wbt.license())
-
- def set_directory(self):
- try:
- self.working_dir = filedialog.askdirectory(
- initialdir=self.exe_path)
- wbt.set_working_dir(self.working_dir)
- except:
- messagebox.showinfo(
- "Warning", "Could not find WhiteboxTools executable file.")
-
- def select_exe(self):
- try:
- filename = filedialog.askopenfilename(initialdir=self.exe_path)
- self.exe_path = path.dirname(path.abspath(filename))
- wbt.set_whitebox_dir(self.exe_path)
- self.refresh_tools()
- except:
- messagebox.showinfo(
- "Warning", "Could not find WhiteboxTools executable file.")
-
- def run_tool(self):
- # wd_str = self.wd.get_value()
- wbt.set_working_dir(self.working_dir)
- # args = shlex.split(self.args_value.get())
-
- args = []
- for widget in self.arg_scroll_frame.winfo_children():
- v = widget.get_value()
- if v:
- args.append(v)
- elif not widget.optional:
- messagebox.showinfo(
- "Error", "Non-optional tool parameter not specified.")
- return
-
- self.print_line_to_output("")
- # self.print_line_to_output("Tool arguments:{}".format(args))
- # self.print_line_to_output("")
- # Run the tool and check the return value for an error
- if wbt.run_tool(self.tool_name, args, self.custom_callback) == 1:
- print("Error running {}".format(self.tool_name))
-
- else:
- self.run_button["text"] = "Run"
- self.progress_var.set(0)
- self.progress_label['text'] = "Progress:"
- self.progress.update_idletasks()
-
- def print_to_output(self, value):
- self.out_text.insert(tk.END, value)
- self.out_text.see(tk.END)
-
- def print_line_to_output(self, value):
- self.out_text.insert(tk.END, value + "\n")
- self.out_text.see(tk.END)
-
- def cancel_operation(self):
- wbt.cancel_op = True
- self.print_line_to_output("Cancelling operation...")
- self.progress.update_idletasks()
-
- def view_code(self):
- webbrowser.open_new_tab(wbt.view_code(self.tool_name).strip())
-
- def update_args_box(self):
- s = ""
- self.current_tool_lbl['text'] = "Current Tool: {}".format(
- self.tool_name)
- # self.spacer['width'] = width=(35-len(self.tool_name))
- for item in wbt.tool_help(self.tool_name).splitlines():
- if item.startswith("-"):
- k = item.split(" ")
- if "--" in k[1]:
- value = k[1].replace(",", "")
- else:
- value = k[0].replace(",", "")
-
- if "flag" in item.lower():
- s = s + value + " "
- else:
- if "file" in item.lower():
- s = s + value + "='{}' "
- else:
- s = s + value + "={} "
-
- # self.args_value.set(s.strip())
-
- def custom_callback(self, value):
- ''' A custom callback for dealing with tool output.
- '''
- if "%" in value:
- try:
- str_array = value.split(" ")
- label = value.replace(
- str_array[len(str_array) - 1], "").strip()
- progress = float(
- str_array[len(str_array) - 1].replace("%", "").strip())
- self.progress_var.set(int(progress))
- self.progress_label['text'] = label
- except ValueError as e:
- print("Problem converting parsed data into number: ", e)
- except Exception as e:
- print(e)
- else:
- self.print_line_to_output(value)
-
- self.update() # this is needed for cancelling and updating the progress bar
-
- def select_all(self, event):
- self.out_text.tag_add(tk.SEL, "1.0", tk.END)
- self.out_text.mark_set(tk.INSERT, "1.0")
- self.out_text.see(tk.INSERT)
- return 'break'
-
-class JsonPayload(object):
- def __init__(self, j):
- self.__dict__ = json.loads(j)
-
-
-def main():
- tool_name = None
- if len(sys.argv) > 1:
- tool_name = str(sys.argv[1])
- wbr = WbRunner(tool_name)
- wbr.mainloop()
-
-
-if __name__ == '__main__':
- main()
\ No newline at end of file
diff --git a/whitebox_example.py b/whitebox_example.py
deleted file mode 100644
index 7f32fc81a..000000000
--- a/whitebox_example.py
+++ /dev/null
@@ -1,143 +0,0 @@
-#!/usr/bin/env python3
-''' This module provides examples of how to call the whitebox_tool script and the
-whitebox-tools geospatial analysis library using Python code.
-'''
-
-# This script is part of the WhiteboxTools geospatial library.
-# Authors: Dr. John Lindsay
-# Created: November 28, 2017
-# Last Modified: Feb. 17, 2018
-# License: MIT
-
-from __future__ import print_function
-import os
-import sys
-from whitebox_tools import WhiteboxTools
-import urllib.request
-
-
-def main():
- ''' main function
- '''
- try:
- wbt = WhiteboxTools()
-
- # Get the root directory of WhiteboxTools source code or executable file
- root_dir = os.path.dirname(os.path.abspath(__file__))
- # WhiteboxTools executable file name for MS Windows
- wbt_win_bin = os.path.join(root_dir, "whitebox_tools.exe")
- # WhiteboxTools executable file name for MacOS/Linux
- wbt_linux_bin = os.path.join(root_dir, "whitebox_tools")
-
- # If the WhiteboxTools executable file (whitbox_tools.exe) is in the same
- # directory as this script, set wbt path to the current directory
- # otherwise, set wbt path to (root_dir + "/target/release/")
- if os.path.isfile(wbt_win_bin) or os.path.isfile(wbt_linux_bin):
- wbt.set_whitebox_dir(root_dir)
- else:
- wbt.set_whitebox_dir(root_dir + "/target/release/") # or simply wbt.exe_path = ...
-
- # Set the working directory. This is the path to the folder containing the data,
- # i.e. files sent to tools as input/output parameters. You don't need to set
- # the working directory if you specify full path names as tool parameters.
- wbt.work_dir = os.path.dirname(
- os.path.abspath(__file__)) + "/testdata/"
-
- # If test datasets do not exist, download them from the WhiteboxTools repo
- if not os.path.exists(wbt.work_dir):
- os.mkdir(wbt.work_dir)
- dem_url = "https://github.com/jblindsay/whitebox-tools/raw/master/testdata/DEM.tif"
- dep_url = "https://github.com/jblindsay/whitebox-tools/raw/master/testdata/DEM.dep"
- urllib.request.urlretrieve(dem_url, "testdata/DEM.tif")
- urllib.request.urlretrieve(dep_url, "testdata/DEM.dep")
-
- # Sets verbose mode (True or False). Most tools will suppress output (e.g. updating
- # progress) when verbose mode is False. The default is True
- # wbt.set_verbose_mode(False) # or simply, wbt.verbose = False
-
- # The most convenient way to run a tool is to use its associated method, e.g.:
- if wbt.elev_percentile("DEM.tif", "output.tif", 15, 15) != 0:
- print("ERROR running tool")
-
- # You may also provide an optional custom callback for processing output from the
- # tool. If you don't provide a callback, and verbose is set to True, tool output
- # will simply be printed to the standard output. Also, notice that each tool has a
- # convenience method. While internally, whitebox_tools.exe uses CamelCase (MeanFilter)
- # to denote tool names, but the Python interface of whitebox_tools.py uses
- # snake_case (mean_filter), according to Python style conventions.
-
- # All of the convenience methods just call the 'run_tool' method, feeding it an
- # args array. This is an alternative way of calling tools:
- tool_name = "elev_percentile"
- args = ["--dem=\"DEM.dep\"",
- "--output=\"DEV_101.dep\"",
- "--filterx=101"]
-
- if wbt.run_tool(tool_name, args, my_callback) != 0:
- print("ERROR running {}".format(tool_name))
-
- # Prints the whitebox-tools help...a listing of available commands
- print(wbt.help())
-
- # Prints the whitebox-tools license
- print(wbt.license())
-
- # Prints the whitebox-tools version
- print("Version information: {}".format(wbt.version()))
-
- # List all available tools in whitebox-tools
- print(wbt.list_tools())
-
- # Lists tools with 'lidar' or 'LAS' in tool name or description.
- print(wbt.list_tools(['lidar', 'LAS']))
-
- # Print the help for a specific tool.
- print(wbt.tool_help("ElevPercentile"))
- # Notice that tool names within WhiteboxTools.exe are CamelCase but
- # you can also use snake_case here, e.g. print(wbt.tool_help("elev_percentile"))
-
- except:
- print("Unexpected error:", sys.exc_info()[0])
- raise
-
-
-def my_callback(out_str):
- ''' Create a custom callback to process the text coming out of the tool.
- If a callback is not provided, it will simply print the output stream.
- A custom callback allows for processing of the output stream.
- '''
- try:
- if not hasattr(my_callback, 'prev_line_progress'):
- my_callback.prev_line_progress = False
- if "%" in out_str:
- str_array = out_str.split(" ")
- label = out_str.replace(str_array[len(str_array) - 1], "").strip()
- progress = int(
- str_array[len(str_array) - 1].replace("%", "").strip())
- if my_callback.prev_line_progress:
- print('{0} {1}%'.format(label, progress), end="\r")
- else:
- my_callback.prev_line_progress = True
- print(out_str)
- elif "error" in out_str.lower():
- print("ERROR: {}".format(out_str))
- my_callback.prev_line_progress = False
- elif "elapsed time (excluding i/o):" in out_str.lower():
- elapsed_time = ''.join(
- ele for ele in out_str if ele.isdigit() or ele == '.')
- units = out_str.lower().replace("elapsed time (excluding i/o):",
- "").replace(elapsed_time, "").strip()
- print("Elapsed time: {0}{1}".format(elapsed_time, units))
- my_callback.prev_line_progress = False
- else:
- if callback.prev_line_progress:
- print('\n{0}'.format(out_str))
- my_callback.prev_line_progress = False
- else:
- print(out_str)
-
- except:
- print(out_str)
-
-
-main()
diff --git a/whitebox_tools.py b/whitebox_tools.py
index 042f03c5b..188f1728b 100644
--- a/whitebox_tools.py
+++ b/whitebox_tools.py
@@ -378,6 +378,7 @@ def list_tools(self, keywords=[]):
+
##############
@@ -3216,6 +3217,26 @@ def breach_single_cell_pits(self, dem, output, callback=None):
args.append("--output='{}'".format(output))
return self.run_tool('breach_single_cell_pits', args, callback) # returns 1 if error
+ def burn_streams_at_roads(self, dem, streams, roads, output, width=None, callback=None):
+ """Burns-in streams at the sites of road embankments.
+
+ Keyword arguments:
+
+ dem -- Input raster digital elevation model (DEM) file.
+ streams -- Input vector streams file.
+ roads -- Input vector roads file.
+ output -- Output raster file.
+ width -- Maximum road embankment width, in map units.
+ callback -- Custom function for handling tool text outputs.
+ """
+ args = []
+ args.append("--dem='{}'".format(dem))
+ args.append("--streams='{}'".format(streams))
+ args.append("--roads='{}'".format(roads))
+ args.append("--output='{}'".format(output))
+ if width is not None: args.append("--width='{}'".format(width))
+ return self.run_tool('burn_streams_at_roads', args, callback) # returns 1 if error
+
def d8_flow_accumulation(self, dem, output, out_type="cells", log=False, clip=False, callback=None):
"""Calculates a D8 flow accumulation raster from an input DEM.
@@ -3670,6 +3691,30 @@ def jenson_snap_pour_points(self, pour_pts, streams, output, snap_dist, callback
args.append("--snap_dist='{}'".format(snap_dist))
return self.run_tool('jenson_snap_pour_points', args, callback) # returns 1 if error
+ def least_cost_breach_depressions(self, dem, output, radius, max_cost=None, min_dist=True, flat_increment=None, fill=True, callback=None):
+ """Breaches the depressions in a DEM using a least-cost pathway method.
+
+ Keyword arguments:
+
+ dem -- Input raster DEM file.
+ output -- Output raster file.
+ radius -- .
+ max_cost -- Optional maximum breach cost (default is Inf).
+ min_dist -- Optional flag indicating whether to minimize breach distances.
+ flat_increment -- Optional elevation increment applied to flat areas.
+ fill -- Optional flag indicating whether to fill any remaining unbreached depressions.
+ callback -- Custom function for handling tool text outputs.
+ """
+ args = []
+ args.append("--dem='{}'".format(dem))
+ args.append("--output='{}'".format(output))
+ args.append("--radius='{}'".format(radius))
+ if max_cost is not None: args.append("--max_cost='{}'".format(max_cost))
+ if min_dist: args.append("--min_dist")
+ if flat_increment is not None: args.append("--flat_increment='{}'".format(flat_increment))
+ if fill: args.append("--fill")
+ return self.run_tool('least_cost_breach_depressions', args, callback) # returns 1 if error
+
def longest_flowpath(self, dem, basins, output, callback=None):
"""Delineates the longest flowpaths for a group of subbasins or watersheds.
@@ -3878,6 +3923,20 @@ def unnest_basins(self, d8_pntr, pour_pts, output, esri_pntr=False, callback=Non
if esri_pntr: args.append("--esri_pntr")
return self.run_tool('unnest_basins', args, callback) # returns 1 if error
+ def upslope_depression_storage(self, dem, output, callback=None):
+ """Estimates the average upslope depression storage depth.
+
+ Keyword arguments:
+
+ dem -- Input raster DEM file.
+ output -- Output raster file.
+ callback -- Custom function for handling tool text outputs.
+ """
+ args = []
+ args.append("--dem='{}'".format(dem))
+ args.append("--output='{}'".format(output))
+ return self.run_tool('upslope_depression_storage', args, callback) # returns 1 if error
+
def watershed(self, d8_pntr, pour_pts, output, esri_pntr=False, callback=None):
"""Identifies the watershed, or drainage basin, draining to a set of target cells.
@@ -5142,6 +5201,22 @@ def standard_deviation_contrast_stretch(self, i, output, stdev=2.0, num_tones=25
# LiDAR Tools #
###############
+ def classify_buildings_in_lidar(self, i, buildings, output, callback=None):
+ """Reclassifies a LiDAR points that lie within vector building footprints.
+
+ Keyword arguments:
+
+ i -- Input LiDAR file.
+ buildings -- Input vector polygons file.
+ output -- Output LiDAR file.
+ callback -- Custom function for handling tool text outputs.
+ """
+ args = []
+ args.append("--input='{}'".format(i))
+ args.append("--buildings='{}'".format(buildings))
+ args.append("--output='{}'".format(output))
+ return self.run_tool('classify_buildings_in_lidar', args, callback) # returns 1 if error
+
def classify_overlap_points(self, i, output, resolution=2.0, filter=False, callback=None):
"""Classifies or filters LAS points in regions of overlapping flight lines.
@@ -5518,7 +5593,7 @@ def lidar_idw_interpolation(self, i=None, output=None, parameter="elevation", re
if maxz is not None: args.append("--maxz='{}'".format(maxz))
return self.run_tool('lidar_idw_interpolation', args, callback) # returns 1 if error
- def lidar_info(self, i, output=None, vlr=False, geokeys=False, callback=None):
+ def lidar_info(self, i, output=None, vlr=True, geokeys=True, callback=None):
"""Prints information about a LiDAR (LAS) dataset, including header, point return frequency, and classification data and information about the variable length records (VLRs) and geokeys.
Keyword arguments:
@@ -5714,6 +5789,40 @@ def lidar_remove_outliers(self, i, output, radius=2.0, elev_diff=50.0, use_media
if classify: args.append("--classify")
return self.run_tool('lidar_remove_outliers', args, callback) # returns 1 if error
+ def lidar_rfb_interpolation(self, i=None, output=None, parameter="elevation", returns="all", resolution=1.0, radius=2.5, exclude_cls=None, minz=None, maxz=None, func_type="ThinPlateSpline", poly_order="none", weight=0.1, callback=None):
+ """Interpolates LAS files using a radial basis function (RFB) scheme. When the input/output parameters are not specified, the tool interpolates all LAS files contained within the working directory.
+
+ Keyword arguments:
+
+ i -- Input LiDAR file (including extension).
+ output -- Output raster file (including extension).
+ parameter -- Interpolation parameter; options are 'elevation' (default), 'intensity', 'class', 'return_number', 'number_of_returns', 'scan angle', 'rgb', 'user data'.
+ returns -- Point return types to include; options are 'all' (default), 'last', 'first'.
+ resolution -- Output raster's grid resolution.
+ radius -- Search Radius.
+ exclude_cls -- Optional exclude classes from interpolation; Valid class values range from 0 to 18, based on LAS specifications. Example, --exclude_cls='3,4,5,6,7,18'.
+ minz -- Optional minimum elevation for inclusion in interpolation.
+ maxz -- Optional maximum elevation for inclusion in interpolation.
+ func_type -- Radial basis function type; options are 'ThinPlateSpline' (default), 'PolyHarmonic', 'Gaussian', 'MultiQuadric', 'InverseMultiQuadric'.
+ poly_order -- Polynomial order; options are 'none' (default), 'constant', 'affine'.
+ weight -- Weight parameter used in basis function.
+ callback -- Custom function for handling tool text outputs.
+ """
+ args = []
+ if i is not None: args.append("--input='{}'".format(i))
+ if output is not None: args.append("--output='{}'".format(output))
+ args.append("--parameter={}".format(parameter))
+ args.append("--returns={}".format(returns))
+ args.append("--resolution={}".format(resolution))
+ args.append("--radius={}".format(radius))
+ if exclude_cls is not None: args.append("--exclude_cls='{}'".format(exclude_cls))
+ if minz is not None: args.append("--minz='{}'".format(minz))
+ if maxz is not None: args.append("--maxz='{}'".format(maxz))
+ args.append("--func_type={}".format(func_type))
+ args.append("--poly_order={}".format(poly_order))
+ args.append("--weight={}".format(weight))
+ return self.run_tool('lidar_rfb_interpolation', args, callback) # returns 1 if error
+
def lidar_segmentation(self, i, output, radius=5.0, norm_diff=10.0, maxzdiff=1.0, classes=False, min_size=1, callback=None):
"""Segments a LiDAR point cloud based on normal vectors.
@@ -7186,26 +7295,6 @@ def zonal_statistics(self, i, features, output=None, stat="mean", out_table=None
# Stream Network Analysis #
###########################
- def burn_streams_at_roads(self, dem, streams, roads, output, width=None, callback=None):
- """Rasterizes vector streams based on Lindsay (2016) method.
-
- Keyword arguments:
-
- dem -- Input raster digital elevation model (DEM) file.
- streams -- Input vector streams file.
- roads -- Input vector roads file.
- output -- Output raster file.
- width -- Maximum road embankment width, in map units.
- callback -- Custom function for handling tool text outputs.
- """
- args = []
- args.append("--dem='{}'".format(dem))
- args.append("--streams='{}'".format(streams))
- args.append("--roads='{}'".format(roads))
- args.append("--output='{}'".format(output))
- if width is not None: args.append("--width='{}'".format(width))
- return self.run_tool('burn_streams_at_roads', args, callback) # returns 1 if error
-
def distance_to_outlet(self, d8_pntr, streams, output, esri_pntr=False, zero_background=False, callback=None):
"""Calculates the distance of stream grid cells to the channel network outlet cell.