├── Data ├── README.md ├── array_100K.npy ├── sample_1000.npy ├── sample_100K.npy ├── sample_10K.npy └── sample_20.npy ├── Free_Tools └── README.md ├── PointTools ├── PointTools.png ├── PointTools.tbx ├── PointTools.tbx.xml ├── PointTools_pro.zip ├── README.md └── Scripts │ ├── __pycache__ │ ├── arcpytools.cpython-35.pyc │ ├── arcpytools.cpython-36.pyc │ ├── arcpytools_pnt.cpython-36.pyc │ ├── radial_sort.cpython-36.pyc │ ├── rotatepnts.cpython-36.pyc │ └── spanning_tree.cpython-36.pyc │ ├── aoi.lyrx │ ├── arcpytools_pnt.py │ ├── circlepnts.py │ ├── closest.py │ ├── closest_od.py │ ├── closetbl.py │ ├── dist_stats.py │ ├── hulls.py │ ├── hulls_editing.py │ ├── mesh_pnts.py │ ├── movepnts.py │ ├── radial_sort.py │ ├── rotatepnts.py │ ├── sort_pnts2line.py │ ├── sortpnts.py │ ├── spaced.py │ ├── spanning_tree.py │ ├── spiral.py │ ├── thumbnails │ ├── Move_points.png │ ├── TableTools.jpg │ ├── circle_pnts.png │ ├── concave_hull_icon.png │ ├── mesh_points.png │ └── standard_distance demo.png │ └── triangulate.py ├── PolygonLineTools ├── Images │ ├── Densify.png │ ├── Phish_Nyet.png │ ├── Polyline_polygon_tools_2020_05_18.png │ ├── README.md │ ├── Split_poly_features.png │ ├── angles.png │ ├── compass.png │ ├── concave_hull_ice01.png │ ├── ice_02.png │ ├── poly_pnts.png │ ├── profile_01.png │ ├── profile_02.png │ ├── profile_map.png │ ├── sampling_grid_dialog.png │ ├── sampling_grid_dialog2.png │ ├── sampling_grid_results.png │ ├── sampling_grids.png │ ├── sector_split.png │ ├── transect_lines.png │ ├── transects.png │ └── triangulate.png ├── Polygon_polyline_Tools.tbx ├── README.md └── Scripts │ ├── arcpytools_plt.py │ ├── code_grid.py │ ├── densify_geom.py │ ├── poly_to_pnts.py │ ├── sampling_grid.py │ ├── split_by_area.py │ ├── split_by_area_notUsed.py │ ├── split_by_sector.py │ ├── split_polys.py │ ├── transect_lines.py │ ├── triangulate.py │ └── vincenty.py ├── README.md ├── TableTools ├── Images │ ├── Frequency_01.png │ ├── Frequency_02.png │ ├── NumPyArray2Table.png │ ├── NumPyArray2Table02.png │ ├── README.md │ ├── Rank_by_fields.png │ ├── Rank_by_fields02.png │ ├── Statistics_01.png │ ├── Statistics_02.png │ ├── Statistics_03.png │ ├── TableTools_2020-05-17.png │ ├── Table_Tools01.png │ ├── Table_Tools02.png │ ├── Table_Tools03.png │ ├── Table_Tools04.png │ ├── Table_to_numpy_array.png │ ├── Table_tools.png │ ├── concat_flds.png │ ├── crosstab.png │ ├── field_calculation_00.png │ ├── field_calculation_01.png │ ├── field_calculation_3.png │ ├── field_name_prefix.png │ ├── find_sequences.png │ ├── find_sequences2.png │ ├── select_columns1.png │ ├── select_columns2.png │ ├── selectbyattributes.png │ ├── sequence05.png │ ├── sequences01.png │ ├── sequences02.png │ ├── sequences03.png │ ├── sequences04.png │ ├── sequential_number_text.png │ └── strip_stuff.png ├── README.md ├── Scripts │ ├── README.md │ ├── angles_.py │ ├── arcpytools.py │ ├── concatenate_flds.py │ ├── cross_tab.py │ ├── cumu_dist.cal │ ├── cumu_dist.py │ ├── excel2tbl.py │ ├── extend_test.py │ ├── field_statistics.py │ ├── frequency.py │ ├── geometry.py │ ├── nested_means.py │ ├── numpyarray2table.py │ ├── pnt_field_calcs.py │ ├── poly_field_calcs.py │ ├── query_reclass.py │ ├── rank_field.py │ ├── rank_flds.py │ ├── sequences.py │ ├── sequential_funcs.py │ ├── sequential_funcs_txt.py │ ├── split_field.py │ ├── strided_funcs.py │ ├── strip_stuff.py │ ├── table2numpy.py │ ├── table_rotate.py │ ├── table_shell_script.py │ ├── table_to_csv.py │ └── table_to_text.py ├── TableTools.tbx └── TableTools_2020_05_17.zip ├── _config.yml ├── _layouts └── default.html ├── arraytools_testing ├── ArrayTools.tbx ├── ReadMe.md ├── array_tools_testing.py ├── art_common.py ├── excel2tbl.py ├── np2tbl.py ├── npy2ras.py ├── ras2npy.py └── tbl2np.py ├── assets └── css │ └── style.scss ├── concavehull ├── Concave_hull.tbx ├── Data │ └── concave_hull.gdb │ │ ├── a00000001.TablesByName.atx │ │ ├── a00000001.gdbindexes │ │ ├── a00000001.gdbtable │ │ ├── a00000001.gdbtablx │ │ ├── a00000002.gdbtable │ │ ├── a00000002.gdbtablx │ │ ├── a00000003.gdbindexes │ │ ├── a00000003.gdbtable │ │ ├── a00000003.gdbtablx │ │ ├── a00000004.CatItemsByPhysicalName.atx │ │ ├── a00000004.CatItemsByType.atx │ │ ├── a00000004.FDO_UUID.atx │ │ ├── a00000004.gdbindexes │ │ ├── a00000004.gdbtable │ │ ├── a00000004.gdbtablx │ │ ├── a00000004.spx │ │ ├── a00000005.CatItemTypesByName.atx │ │ ├── a00000005.CatItemTypesByParentTypeID.atx │ │ ├── a00000005.CatItemTypesByUUID.atx │ │ ├── a00000005.gdbindexes │ │ ├── a00000005.gdbtable │ │ ├── a00000005.gdbtablx │ │ ├── a00000006.CatRelsByDestinationID.atx │ │ ├── a00000006.CatRelsByOriginID.atx │ │ ├── a00000006.CatRelsByType.atx │ │ ├── a00000006.FDO_UUID.atx │ │ ├── a00000006.gdbindexes │ │ ├── a00000006.gdbtable │ │ ├── a00000006.gdbtablx │ │ ├── a00000007.CatRelTypesByBackwardLabel.atx │ │ ├── a00000007.CatRelTypesByDestItemTypeID.atx │ │ ├── a00000007.CatRelTypesByForwardLabel.atx │ │ ├── a00000007.CatRelTypesByName.atx │ │ ├── a00000007.CatRelTypesByOriginItemTypeID.atx │ │ ├── a00000007.CatRelTypesByUUID.atx │ │ ├── a00000007.gdbindexes │ │ ├── a00000007.gdbtable │ │ ├── a00000007.gdbtablx │ │ ├── a00000009.gdbindexes │ │ ├── a00000009.gdbtable │ │ ├── a00000009.gdbtablx │ │ ├── a00000009.spx │ │ ├── a0000000a.gdbindexes │ │ ├── a0000000a.gdbtable │ │ ├── a0000000a.gdbtablx │ │ ├── a0000000a.spx │ │ ├── gdb │ │ └── timestamps ├── README.md ├── Scripts │ ├── arcpytools_ch.py │ └── hulls.py └── concave_hull_icon.png ├── field_calculator ├── Field_Calculator_defs_2017_07_03.py ├── Field_Calculator_main.py ├── angle_between.py ├── angles_.py ├── azimuth_sequential_pnts.py ├── azimuth_to.py ├── cal_files │ ├── angle_between.cal │ ├── angles.cal │ ├── area_sqkm.cal │ ├── azimuth_to.cal │ ├── dist_to.cal │ ├── fld_calc_cursor.cal │ ├── index │ ├── length_km.cal │ ├── natural_pad.cal │ ├── natural_sort.cal │ ├── part_count.cal │ ├── pnt_along.cal │ ├── point_count.cal │ ├── rand_float.cal │ ├── rand_int.cal │ ├── rand_norm.cal │ ├── seq_group.cal │ ├── seq_num_max.cal │ ├── sequence.cal │ ├── sequential_count.cal │ └── sequential_id.cal ├── coordinate_defs.py ├── cumu_dist.py ├── date_time_defs.py ├── dist_to.py ├── natural_pad.py ├── natural_sort.cal.py ├── natural_sort.py ├── pad_date.py ├── pnt_along.py ├── rand_float.py ├── rand_int.py ├── rand_norm.py ├── seq_dup2.py ├── seq_group.py ├── sequential_count.py ├── sequential_dups.py ├── sequential_id.py ├── shift_features.py ├── string_defs.py ├── strip_concatenate.py └── strip_time.py ├── frequency_statistics ├── Frequency_Statistics.tbx ├── README.md └── frequency.py ├── geometry_tools ├── Densify.png ├── Phish_Nyet.png ├── angles.png ├── angles.py ├── densify_geom.py ├── geom_helper.py ├── index └── sampling_grid.py ├── sampling_grid ├── Readme.md ├── SamplingGrid.tbx ├── Sampling_grids.png ├── sampling_grid.py ├── sampling_grid_dialog.png └── sampling_grid_dialog2.png ├── statistics ├── Images │ ├── Column_Statistics.png │ └── Column_Statistics02.png ├── README.md ├── Statistics.tbx ├── Statistics.zip ├── field_stats.py └── rank_field.py └── triangulation_tools ├── Scripts ├── __pycache__ │ ├── arcpytools_pnt.cpython-36.pyc │ └── triangulate.cpython-36.pyc ├── arcpytools_pnt.py └── triangulate.py ├── triangulation_tools.tbx ├── triangulation_tools.tbx.xml └── voronoi.png /Data/README.md: -------------------------------------------------------------------------------- 1 | ## **NumPy arrays** 2 | 3 | Just some structured array data for testing purposes. 4 | 5 | Download and load with 6 | 7 | *np.load(array)* 8 | 9 | The arrays contain the same information created with a fixed proportion of different sizes. For example... 10 | 11 | 1. *sample_100k.npy* (7,423 KB) 12 | 13 | * 100,000 records 14 | * shape = (100000,) 15 | * dtype=[('OBJECTID', ' 2 | 20170305120241001.0TRUE1500000005000Tools for working with point data. 3 | Latest version 2018_12_29<DIV STYLE="text-align:Left;"><DIV><DIV><P><SPAN>A series of tools exploiting numpy capabilities applied to point featureclasses/shapefiles</SPAN></P></DIV></DIV></DIV>Dan_Patterson@carleton.caPoint toolspointsnumpynumpyarraytofeatureclassfeatureclasstonumpyarraymoveshiftnear<DIV STYLE="text-align:Left;"><DIV><DIV><P><SPAN>None</SPAN></P></DIV></DIV></DIV>20181229 4 | -------------------------------------------------------------------------------- /PointTools/PointTools_pro.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dan-Patterson/Tools_for_ArcGIS_Pro/6315d33469107b96c691a71b52ec695045a388c1/PointTools/PointTools_pro.zip -------------------------------------------------------------------------------- /PointTools/README.md: -------------------------------------------------------------------------------- 1 | **PointTools** 2 | ---- 3 | 4 | Tools for working with point featureclasses. 5 | 6 | Developed in ArcGIS PRO. 7 | 8 | Most tools use NumPy for their data processing (numpy >= 1.16 should be fine). 9 | 10 | *Notes* 11 | 12 | - Download the zip file and unzip in a folder. 13 | - Add the toolbox to your project. 14 | 15 | 16 | The scripts can be examined here without having to download the zip. 17 | 18 | **Toolbox, Toolsets and Tools** 19 | 20 | 21 | 22 | There are 3 toolsets: 23 | - Alter 24 | - Analysis 25 | - Creation 26 | 27 | Some of these would normally require an Advanced license for ArcGIS Pro, however, the functionality can largely be replicated using python, numpy or scipy and arcpy. 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /PointTools/Scripts/__pycache__/arcpytools.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dan-Patterson/Tools_for_ArcGIS_Pro/6315d33469107b96c691a71b52ec695045a388c1/PointTools/Scripts/__pycache__/arcpytools.cpython-35.pyc -------------------------------------------------------------------------------- /PointTools/Scripts/__pycache__/arcpytools.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dan-Patterson/Tools_for_ArcGIS_Pro/6315d33469107b96c691a71b52ec695045a388c1/PointTools/Scripts/__pycache__/arcpytools.cpython-36.pyc -------------------------------------------------------------------------------- /PointTools/Scripts/__pycache__/arcpytools_pnt.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dan-Patterson/Tools_for_ArcGIS_Pro/6315d33469107b96c691a71b52ec695045a388c1/PointTools/Scripts/__pycache__/arcpytools_pnt.cpython-36.pyc -------------------------------------------------------------------------------- /PointTools/Scripts/__pycache__/radial_sort.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dan-Patterson/Tools_for_ArcGIS_Pro/6315d33469107b96c691a71b52ec695045a388c1/PointTools/Scripts/__pycache__/radial_sort.cpython-36.pyc -------------------------------------------------------------------------------- /PointTools/Scripts/__pycache__/rotatepnts.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dan-Patterson/Tools_for_ArcGIS_Pro/6315d33469107b96c691a71b52ec695045a388c1/PointTools/Scripts/__pycache__/rotatepnts.cpython-36.pyc -------------------------------------------------------------------------------- /PointTools/Scripts/__pycache__/spanning_tree.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dan-Patterson/Tools_for_ArcGIS_Pro/6315d33469107b96c691a71b52ec695045a388c1/PointTools/Scripts/__pycache__/spanning_tree.cpython-36.pyc -------------------------------------------------------------------------------- /PointTools/Scripts/circlepnts.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | """ 3 | :Script: createcompass.py 4 | :Author Dan_Patterson@carleton.ca 5 | :Modified: 2017-04-02 6 | : if north angle is needed, you can use this to convert 7 | :if fromNorth: 8 | : ang = np.mod((450.0 - ang), 360.) 9 | """ 10 | 11 | # -------------------------------------------------------------------------- 12 | import sys 13 | import numpy as np 14 | import arcpy 15 | from arcpytools_pnt import tweet 16 | 17 | ft = {'bool': lambda x: repr(x.astype(np.int32)), 18 | 'float_kind': '{: 0.3f}'.format} 19 | np.set_printoptions(edgeitems=10, linewidth=80, precision=2, 20 | suppress=True, threshold=100, formatter=ft) 21 | np.ma.masked_print_option.set_display('-') # change to a single - 22 | 23 | script = sys.argv[0] # print this should you need to locate the script 24 | 25 | 26 | def _circle(radius=10, theta=22.5, xc=0.0, yc=0.0): 27 | """Produce a circle/ellipse depending on parameters. 28 | : radius - distance from centre 29 | : theta - either a single value to form angles about a circle or 30 | : - a list or tuple of the desired angles 31 | """ 32 | angles = np.deg2rad(np.arange(180.0, -180.0-theta, step=-theta)) 33 | x_s = radius*np.cos(angles) + xc # X values 34 | y_s = radius*np.sin(angles) + yc # Y values 35 | pnts = np.c_[x_s, y_s] 36 | return pnts 37 | 38 | 39 | # -------------------------------------------------------------------------- 40 | inFC = sys.argv[1] # r"C:\Data\points\points.gdb\fishnet_label" 41 | outFC = sys.argv[2] # r"C:\Data\points\pnts2.shp" 42 | radius = float(sys.argv[3]) # radius = 2 43 | theta = float(sys.argv[4]) 44 | a = arcpy.da.FeatureClassToNumPyArray(inFC, ["SHAPE@X", "SHAPE@Y"]) 45 | 46 | frmt = """... {} ... 47 | :Input featureclass : {} 48 | :Output featureclass: {} 49 | :Radius {}, angle step {} 50 | :Points: 51 | {} 52 | : 53 | """ 54 | args = [script, inFC, outFC, radius, theta, a] 55 | msg = frmt.format(*args) 56 | tweet(msg) 57 | # ---- option (1) read X and Y separately, then reset the dtype names 58 | # ---- get the circle values, stack and set dtype 59 | # or a list like... theta = [0, 90, 180, 270] 60 | # theta = [0, 90, 180, 270] 61 | 62 | a.dtype = [('X', ' 14 | 15 | 16 | The poly* to points retains the original poly feature ID then adds a new column with the point ID for that feature. The labelling was simply a concatenation of the two bits of information. 17 | 18 | 19 | 20 | ## Split Poly`*` Features`*` 21 | 22 | Split poly`*` features based on a width or height factor. 23 | 24 | 25 | 26 | ## Sampling Grids 27 | 28 | Produce sampling grids as rectangles, hexagons (two types) and one triangle form. 29 | Rotation is supported and the features are given a spreadsheet-line labeling code. 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /PolygonLineTools/Scripts/code_grid.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | code_grid 4 | ========= 5 | 6 | Script : code_grid.py 7 | 8 | Author : Dan_Patterson@carleton.ca 9 | 10 | Modified : 2018-08-19 11 | 12 | Purpose: produce a spreadsheet-like numbering system for 'grid' cells 13 | 14 | This use padding A01 to facilitate sorting. 15 | If you want a different system change 16 | >>> >>> "{}{}".format(UC[c], r) # A1 to whatever, no padding 17 | >>> "{}{:02.0f}".format(UC[c], r) # A01 to ..99 18 | >>> "{}{:03.0f}".format(UC[c], r) # A001 to A999 19 | >>> # etc 20 | >>> c0 = code_grid(cols=5, rows=3, zero_based=False, shaped=True, bottom_up=False) 21 | [['A01' 'B01' 'C01' 'D01' 'E01'] 22 | ['A02' 'B02' 'C02' 'D02' 'E02'] 23 | ['A03' 'B03' 'C03' 'D03' 'E03']] 24 | 25 | --------------------------------------------------------------------- 26 | """ 27 | 28 | import sys 29 | import numpy as np 30 | 31 | ft = {'bool': lambda x: repr(x.astype(np.int32)), 32 | 'float_kind': '{: 0.3f}'.format} 33 | np.set_printoptions(edgeitems=10, linewidth=80, precision=2, suppress=True, 34 | threshold=100, formatter=ft) 35 | np.ma.masked_print_option.set_display('-') # change to a single - 36 | 37 | script = sys.argv[0] # print this should you need to locate the script 38 | 39 | def code_grid(cols=1, rows=1, zero_based=False, shaped=True, bottom_up=False): 40 | """produce spreadsheet like labelling, either zero or 1 based 41 | : zero - A0,A1 or ones - A1, A2.. 42 | : dig = list('0123456789') # string.digits 43 | : import string .... string.ascii_uppercase 44 | """ 45 | alph = list(" ABCDEFGHIJKLMNOPQRSTUVWXYZ") 46 | UC = [("{}{}").format(alph[i], alph[j]).strip() 47 | for i in range(27) 48 | for j in range(1,27)] 49 | z = [1, 0][zero_based] 50 | rc = [1, 0][zero_based] 51 | c = ["{}{:02.0f}".format(UC[c], r) # pull in the column heading 52 | for r in range(z, rows + rc) # label in the row letter 53 | for c in range(cols)] # label in the row number 54 | c = np.asarray(c) 55 | if shaped: 56 | c = c.reshape(rows, cols) 57 | if bottom_up: 58 | c = np.flipud(c) 59 | return c 60 | 61 | # ---------------------------------------------------------------------- 62 | # __main__ .... code section 63 | if __name__ == "__main__": 64 | """Optionally... 65 | : - print the script source name. 66 | : - run the _demo 67 | """ 68 | c0 = code_grid(cols=3, rows=100, zero_based=False, shaped=True, bottom_up=False) 69 | print(c0) 70 | -------------------------------------------------------------------------------- /PolygonLineTools/Scripts/poly_to_pnts.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | poly_to_pnts.py 4 | =============== 5 | 6 | Script : poly_to_pnts.py 7 | 8 | Author : Dan_Patterson@carleton.ca 9 | 10 | Modified : 2018-09-12 11 | 12 | Purpose: 13 | -------- 14 | Tools for working with numpy arrays. This converts poly* features 15 | to points 16 | 17 | References 18 | ---------- 19 | ``_. 20 | ``_. 21 | --------------------------------------------------------------------- 22 | """ 23 | 24 | import sys 25 | import numpy as np 26 | from arcpytools_plt import fc_array, fc_info, tweet #, frmt_rec, _col_format 27 | import arcpy 28 | 29 | ft = {'bool': lambda x: repr(x.astype(np.int32)), 30 | 'float_kind': '{: 0.3f}'.format} 31 | np.set_printoptions(edgeitems=10, linewidth=80, precision=2, suppress=True, 32 | threshold=100, formatter=ft) 33 | np.ma.masked_print_option.set_display('-') # change to a single - 34 | 35 | script = sys.argv[0] # print this should you need to locate the script 36 | 37 | def append_fld(arr, name='Pnt_id', data=None): 38 | """Append an id field to a subarray of points. 39 | Emulated after recfunctions append_fields 40 | """ 41 | dt = arr.dtype.descr + [(name, '>> from angles import angles_poly 20 | 21 | In the field calculator calling function, feed it the shape field 22 | angles_poly(!Shape!) 23 | """ 24 | 25 | def angles_poly(a, inside=True, in_deg=True, kind="sum"): 26 | """Sequential angles from a poly* shape 27 | NOTE: this line.... angle = np.sum(angles) 28 | can be changed to `np.min`, `np.max` or others 29 | depending on what needs to be returned 30 | """ 31 | import numpy as np 32 | a = a.getPart() 33 | a =np.asarray([[i.X, i.Y] for j in a for i in j]) 34 | if len(a) < 2: 35 | return None 36 | elif len(a) == 2: # **** check 37 | ba = a[1] - a[0] 38 | return np.arctan2(*ba[::-1]) 39 | else: 40 | angles = [] 41 | if np.allclose(a[0], a[-1]): # closed loop 42 | a = a[:-1] 43 | r = (-1,) + tuple(range(len(a))) + (0,) 44 | else: 45 | r = tuple(range(len(a))) 46 | for i in range(len(r)-2): 47 | p0, p1, p2 = a[r[i]], a[r[i+1]], a[r[i+2]] 48 | ba = p1 - p0 49 | bc = p1 - p2 50 | cr = np.cross(ba, bc) 51 | dt = np.dot(ba, bc) 52 | ang = np.arctan2(np.linalg.norm(cr), dt) 53 | if not np.allclose(ang, np.pi): # check for extra vertices 54 | angles.append(ang) 55 | if in_deg: 56 | angles = np.degrees(angles) 57 | if kind == "sum": 58 | angle = np.sum(angles) 59 | elif kind == "min": 60 | angle = np.min(angles) 61 | elif kind == "max": 62 | angle = np.max(angles) 63 | return angle 64 | #__esri_field_calculator_splitter__ 65 | #angles_poly(!Shape!) -------------------------------------------------------------------------------- /TableTools/Scripts/cumu_dist.cal: -------------------------------------------------------------------------------- 1 | """ input shape field: returns cumulative distance between points 2 | dist_cumu(!Shape!) #enter into the expression box""" 3 | import math 4 | x0 = 0.0; y0 = 0.0; distance = 0.0 5 | def dist_cumu(shape): 6 | global x0; global y0; global distance 7 | x = shape.firstpoint.X; y = shape.firstpoint.Y 8 | if x0 == 0.0 and y0 == 0.0: 9 | x0 = x; y0 = y 10 | distance += math.sqrt((x - x0)**2 + (y - y0)**2) 11 | x0 = x; y0 = y 12 | return distance 13 | __esri_field_calculator_splitter__ 14 | dist_cumu(!Shape!) -------------------------------------------------------------------------------- /TableTools/Scripts/cumu_dist.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ input shape field: returns cumulative distance between points 3 | dist_cumu(!Shape!) #enter into the expression box""" 4 | import math 5 | x0 = 0.0; y0 = 0.0; distance = 0.0 6 | def dist_cumu(shape): 7 | global x0; global y0; global distance 8 | x = shape.firstpoint.X; y = shape.firstpoint.Y 9 | if x0 == 0.0 and y0 == 0.0: 10 | x0 = x; y0 = y 11 | distance += math.sqrt((x - x0)**2 + (y - y0)**2) 12 | x0 = x; y0 = y 13 | return distance -------------------------------------------------------------------------------- /TableTools/Scripts/extend_test.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | """ 3 | :Script: .py 4 | :Author: Dan.Patterson@carleton.ca 5 | :Modified: 2017-xx-xx 6 | :Purpose: tools for working with numpy arrays 7 | :Useage: 8 | : 9 | :References: 10 | : 11 | :---------------------------------------------------------------------: 12 | """ 13 | # ---- imports, formats, constants ---- 14 | import sys 15 | import numpy as np 16 | import arcpy 17 | 18 | 19 | ft = {'bool': lambda x: repr(x.astype('int32')), 20 | 'float': '{: 0.3f}'.format} 21 | 22 | np.set_printoptions(edgeitems=10, linewidth=80, precision=2, suppress=True, 23 | threshold=100, formatter=ft) 24 | np.ma.masked_print_option.set_display('-') # change to a single - 25 | 26 | script = sys.argv[0] # print this should you need to locate the script 27 | 28 | def tweet(msg): 29 | """Print a message for both arcpy and python. 30 | : msg - a text message 31 | """ 32 | m = "\n{}\n".format(msg) 33 | arcpy.AddMessage(m) 34 | print(m) 35 | print(arcpy.GetMessages()) 36 | 37 | 38 | #in_fc = r"C:\Git_Dan\a_Data\testdata.gdb\Polygon_pnts" 39 | # arcpy.env.workspace = "C:/data/base.gdb" 40 | in_tbl = sys.argv[1] 41 | in_flds = sys.argv[2] 42 | out_fld = sys.argv[3] 43 | 44 | if ';' in in_flds: 45 | in_flds = in_flds.split(';') 46 | else: 47 | in_flds = [in_flds] 48 | 49 | desc = arcpy.da.Describe(in_tbl) 50 | tbl_path = desc['path'] 51 | fnames = [i.name for i in arcpy.ListFields(in_tbl)] 52 | if out_fld in fnames: 53 | out_fld += 'dup' 54 | out_fld = arcpy.ValidateFieldName(out_fld, tbl_path) 55 | args = [in_tbl, in_flds, out_fld, tbl_path] 56 | msg = "in_tbl {}\nin_fld {}\nout_fld {}\ntbl_path {}".format(*args) 57 | tweet(msg) 58 | oid = 'OBJECTID' 59 | vals = [oid] + in_flds 60 | arr = arcpy.da.TableToNumPyArray(in_tbl, vals) 61 | tweet("{!r:}".format(arr)) 62 | for v in vals: 63 | fix_null = np.where(arr[v] == 'None', '', arr[v]) 64 | arr[v] = fix_null 65 | tweet("fixing vals...{}\n{}".format(v, arr[v])) 66 | arr_sort = np.sort(arr, order=in_flds) 67 | # dt = [(oid, '`_. 23 | 24 | Notes 25 | ----- 26 | 27 | >>> to_array = arcpy.da.TableToNumPyArray(r"C:\folder\sample.dbf", "*") 28 | >>> arcpy.da.NumPyArrayToTable(from_array, r"C:\folder_tbl\test.gdb\out") 29 | 30 | Dev Info 31 | 32 | tbx - C:\GIS\Tools_scripts\Table_tools\Table_tools.tbx 33 | script - C:\GIS\Tools_scripts\Table_tools\Scripts\frequency.py 34 | arcpy.Tabletools.Frequency("polygon_demo", 35 | r"C:\GIS\Tools_scripts\Table_tools\Table_tools.gdb\f2", 36 | "Test;main_part", None) 37 | :---------------------------------------------------------------------: 38 | """ 39 | # ---- imports, formats, constants ---- 40 | import sys 41 | import numpy as np 42 | import arcpy 43 | 44 | import numpy.lib.recfunctions as rfn 45 | 46 | ft = {'bool': lambda x: repr(x.astype('int32')), 47 | 'float': '{: 0.3f}'.format} 48 | np.set_printoptions(edgeitems=10, linewidth=80, precision=2, suppress=True, 49 | threshold=100, formatter=ft) 50 | np.ma.masked_print_option.set_display('-') # change to a single - 51 | 52 | script = sys.argv[0] # print this should you need to locate the script 53 | 54 | 55 | def freq(a, flds=None): 56 | """Frequency and crosstabulation 57 | 58 | Parameters 59 | ---------- 60 | a : array 61 | A structured array 62 | flds : field 63 | Fields to use in the analysis 64 | 65 | Notes 66 | ----- 67 | : (1) slice the input array by the classification fields 68 | : (2) sort the sliced array using the flds as sorting keys 69 | : (3) use unique on the sorted array to return the results 70 | : (4) a quick histogram to get the counts until numpy 1.12 can be used 71 | : ... then ship the results back. only uni and vals is needed. The 72 | : rest is for testing and future work. 73 | >>> np.unique(ar, return_index=False, return_inverse=False, 74 | ... return_counts=False, axis=None) 75 | """ 76 | a = a[flds] # (1) 77 | idx = np.argsort(a, axis=0, order=flds) # (2) 78 | a_sort = a[idx] 79 | uni, cnts = np.unique(a_sort, return_counts=True) # (3) 80 | out = rfn.append_fields(uni, names="Counts", data=cnts, usemask=False) 81 | return out 82 | 83 | 84 | if len(sys.argv) == 1: 85 | testing = True 86 | in_tbl = r"C:\Arc_projects\Table_tools\Table_tools.gdb\SamplingGrids" 87 | all_flds = "*" 88 | cls_flds = 'Row_class;Col_class' 89 | else: 90 | testing = False 91 | in_tbl = sys.argv[1] # input table 92 | out_tbl = sys.argv[2] # results table 93 | cls_flds = sys.argv[3] # classification field(s) 94 | 95 | # ---- common tasks 96 | cls_flds = cls_flds.split(";") # tidy up the two field inputs 97 | a = arcpy.da.TableToNumPyArray(in_tbl, "*") # use the full array's data 98 | 99 | out = freq(a, cls_flds) # do freq analysis 100 | 101 | # create the output array and return the table 102 | if not testing: 103 | arcpy.da.NumPyArrayToTable(out, out_tbl) 104 | 105 | # ---------------------------------------------------------------------- 106 | # __main__ .... code section 107 | if __name__ == "__main__": 108 | """Do nothing for now. 109 | """ 110 | 111 | -------------------------------------------------------------------------------- /TableTools/Scripts/geometry.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | geometry 4 | ======== 5 | 6 | Requires: 7 | --------- 8 | 9 | This function is called by poly_field_calcs.py from the Table Tools toolset 10 | It is designed to be imported and used in the field calculator 11 | 12 | There is a check for redundant vertices which affects the sum and max angles. 13 | Specifically, 3 points on a 2 point line will have 180 degree max 14 | 15 | Useage: 16 | ------ 17 | In the calling script use 18 | 19 | >>> from angles import angles_poly 20 | 21 | In the field calculator calling function, feed it the shape field 22 | angles_poly(!Shape!) 23 | """ 24 | 25 | import numpy as np 26 | import math 27 | from arcpytools import fc_info, tweet 28 | import arcpy 29 | 30 | def angles_(a, in_deg=True, kind="sum"): 31 | """Sequential angles from a poly* shape 32 | 33 | `a` - shape 34 | A shape from the shape field in a geodatabase 35 | 36 | """ 37 | import numpy as np 38 | a = a.getPart() 39 | a = np.asarray([[i.X, i.Y] for j in a for i in j]) 40 | if len(a) < 2: 41 | return None 42 | elif len(a) == 2: 43 | ba = a[1] - a[0] 44 | return np.arctan2(*ba[::-1]) 45 | a0 = a[0:-2] 46 | a1 = a[1:-1] 47 | a2 = a[2:] 48 | ba = a1 - a0 49 | bc = a1 - a2 50 | cr = np.cross(ba, bc) 51 | dt = np.einsum('ij,ij->i', ba, bc) 52 | # ang = np.arctan2(np.linalg.norm(cr), dt) 53 | ang = np.arctan2(cr, dt) 54 | two_pi = np.pi*2. 55 | angles = np.where(ang<0, ang + two_pi, ang) 56 | if in_deg: 57 | angles = np.degrees(angles) 58 | if in_deg: 59 | angles = np.degrees(angles) 60 | if kind == "sum": 61 | angle = np.sum(angles) 62 | elif kind == "min": 63 | angle = np.min(angles) 64 | elif kind == "max": 65 | angle = np.max(angles) 66 | return angle 67 | 68 | 69 | def lengths_(a, kind="avg"): 70 | """poly* side lengths. 71 | Options include "min length", "max length", "avg length" 72 | """ 73 | import numpy as np 74 | a = a.getPart() 75 | a = np.asarray([[i.X, i.Y] for j in a for i in j]) 76 | if np.allclose(a[0], a[-1]): # closed loop 77 | a = a[:-1] 78 | a0 = a[:-1] 79 | b0 = a[1:] 80 | diff = b0 - a0 81 | s = np.power(diff, 2) 82 | d = np.sqrt(s[:, 0] + s[:, 1]) 83 | if kind == "avg": 84 | leng = np.mean(d) 85 | elif kind == "min": 86 | leng = np.min(d) 87 | elif kind == "max": 88 | leng = np.max(d) 89 | return leng 90 | 91 | #__esri_field_calculator_splitter__ 92 | #angles_poly(!Shape!) 93 | 94 | 95 | def to_array(in_fc): 96 | """Extract the shapes and produce a coordinate array. 97 | """ 98 | shp_fld, oid_fld, shp_type, SR = fc_info(in_fc) 99 | in_flds = [oid_fld] + ['SHAPE@X', 'SHAPE@Y'] 100 | a = arcpy.da.FeatureClassToNumPyArray(in_fc, in_flds) 101 | a = a[['SHAPE@X', 'SHAPE@Y']] 102 | a = a.view(np.float64).reshape(a.shape[0], 2) 103 | return a 104 | 105 | -------------------------------------------------------------------------------- /TableTools/Scripts/numpyarray2table.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | """ 3 | :Script: numpyarray2table.py 4 | :Author: Dan.Patterson@carleton.ca 5 | :Modified: 2018-02-11 6 | :Purpose: tools for working with numpy arrays 7 | :Useage: 8 | : 9 | :References: 10 | : - Derived from python snippet output... 11 | : in_arr = 'C:/Temp/x.npy' 12 | : out_gdb = 'C:/GIS/Tools_scripts/Table_tools/Table_tools.gdb' 13 | : out_name = 'sample_1000_npy' 14 | : arcpy.Tabletools.NumPyArrayToTable(in_arr, out_gdb, out_name) 15 | :---------------------------------------------------------------------: 16 | """ 17 | # ---- imports, formats, constants ---- 18 | import sys 19 | from textwrap import dedent 20 | import numpy as np 21 | import arcpy 22 | 23 | ft = {'bool': lambda x: repr(x.astype(np.int32)), 24 | 'float_kind': '{: 0.3f}'.format} 25 | np.set_printoptions(edgeitems=10, linewidth=80, precision=2, suppress=True, 26 | threshold=100, formatter=ft) 27 | np.ma.masked_print_option.set_display('-') # change to a single - 28 | 29 | script = sys.argv[0] # print this should you need to locate the script 30 | 31 | 32 | def tweet(msg): 33 | """Print a message for both arcpy and python. 34 | : msg - a text message 35 | """ 36 | m = "\n{}\n".format(msg) 37 | arcpy.AddMessage(m) 38 | print(m) 39 | 40 | 41 | # ---- Run options: _demo or from _tool 42 | # 43 | def _demo(): 44 | """Code to run if in demo mode 45 | """ 46 | a = np. array(['1, 2, 3, 4, 5', 'a, b, c', '6, 7, 8, 9', 47 | 'd, e, f, g, h', '10, 11, 12, 13']) 48 | return a 49 | 50 | 51 | def _tool(): 52 | """run when script is from a tool 53 | """ 54 | in_arr = sys.argv[1] 55 | out_gdb = sys.argv[2] 56 | out_name = sys.argv[3] 57 | # 58 | arcpy.env.workspace = out_gdb 59 | tbls = arcpy.ListTables() 60 | out_name = arcpy.ValidateTableName(out_name) 61 | if out_name in tbls: 62 | out_name += '_dup' 63 | # 64 | # ---- call section for processing function 65 | # 66 | a = np.load(in_arr) 67 | in_table = "\\".join([out_gdb, out_name]) 68 | # ---- where_clause= skip_nulls= null_value=) 69 | arcpy.da.NumPyArrayToTable(a, in_table) 70 | arcpy.MakeTableView_management(in_table, out_name) 71 | # 72 | args = [in_arr, out_gdb, out_name] 73 | msg = """ 74 | :------------------------------------------------------------ 75 | Input array... {} 76 | Output gdb.... {} 77 | Output name... {} 78 | 79 | Conversion complete... 80 | Add the table manually if you want to see it... 81 | :------------------------------------------------------------ 82 | """ 83 | msg = dedent(msg).format(*args) 84 | tweet(msg) 85 | 86 | 87 | # ---------------------------------------------------------------------- 88 | # .... final code section producing the featureclass and extendtable 89 | if len(sys.argv) == 1: 90 | testing = True 91 | arrs = _demo() 92 | frmt = "Result...\n{}" 93 | print(frmt.format(arrs)) 94 | else: 95 | testing = False 96 | _tool() 97 | # 98 | if not testing: 99 | tweet('\nConversion done...') 100 | 101 | 102 | # ---------------------------------------------------------------------- 103 | # __main__ .... code section 104 | if __name__ == "__main__": 105 | """Optionally... 106 | : - print the script source name. 107 | : - run the _demo 108 | """ 109 | # print("Script... {}".format(script)) 110 | a = _demo() 111 | -------------------------------------------------------------------------------- /TableTools/Scripts/pnt_field_calcs.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | pnt_field_calcs 4 | ================ 5 | 6 | Script : pnt_field_calcs.py 7 | 8 | Author : Dan_Patterson@carleton.ca 9 | 10 | Modified : 2018-07-25 11 | 12 | Purpose : tools for working ArcGIS Pro field calculator 13 | 14 | Useage: 15 | ------- 16 | 17 | 18 | **Requires** 19 | ------------ 20 | 21 | 22 | **Notes** 23 | --------- 24 | 25 | **Basic array information** 26 | 27 | """ 28 | # ---- imports, formats, constants ------------------------------------------- 29 | import sys 30 | import inspect 31 | from textwrap import dedent, indent 32 | import numpy as np 33 | import arcpy 34 | 35 | arcpy.env.overwriteOutput = True 36 | 37 | script = sys.argv[0] 38 | 39 | # ---- simple functions ------------------------------------------------------ 40 | 41 | __all__ = [] 42 | 43 | if len(sys.argv) == 1: 44 | testing = True 45 | create_output = False 46 | in_tbl = "/pnts_2k_normal" 47 | gdb = "/Table_tools.gdb" 48 | flder = "/".join(script.split("/")[:-2]) 49 | wrkspace = flder + gdb 50 | in_tbl = wrkspace + in_tbl 51 | in_fld = None 52 | else: 53 | testing = False 54 | create_output = True 55 | in_tbl = sys.argv[1] 56 | in_fld = sys.argv[2] 57 | exp_key = sys.argv[3] 58 | 59 | # ---- Do the work ----------------------------------------------------------- 60 | # 61 | desc = arcpy.da.Describe(in_tbl) 62 | wrkspace = desc['path'] 63 | 64 | # ---- Expression functions 65 | fld_name = in_fld 66 | 67 | if exp_key == "cumulative distance": 68 | from geometry import dist_cumu 69 | if inspect.isfunction(dist_cumu): 70 | lines, ln_num = inspect.getsourcelines(dist_cumu) 71 | code = "".join(["{}".format(line) for line in lines]) 72 | fld_expr = "dist_cumu(!Shape!)" 73 | fld_name = "Cumu_dist" 74 | args = [fld_name, fld_expr, code] 75 | elif exp_key == "distance between": 76 | from geometry import dist_between 77 | if inspect.isfunction(dist_between): 78 | lines, ln_num = inspect.getsourcelines(dist_between) 79 | code = "".join(["{}".format(line) for line in lines]) 80 | fld_expr = "dist_between(!Shape!)" 81 | fld_name = "Dist_btwn" 82 | args = [fld_name, fld_expr, code] 83 | 84 | fld_name, fld_expr, code = args 85 | 86 | arcpy.MakeTableView_management( 87 | in_table=in_tbl, 88 | out_view="tbl_view", 89 | workspace=wrkspace) 90 | 91 | if in_fld in (None, "", " "): 92 | fld_name = fld_name 93 | else: 94 | fld_name = in_fld 95 | fld_name = arcpy.ValidateFieldName(fld_name) 96 | arcpy.AddField_management( 97 | "tbl_view", 98 | field_name=fld_name, 99 | field_type="DOUBLE", 100 | field_is_nullable="NULLABLE") 101 | 102 | arcpy.CalculateField_management( 103 | in_table="tbl_view", 104 | field=fld_name, 105 | expression=fld_expr, 106 | code_block=code) 107 | 108 | del in_fld, in_tbl, arcpy 109 | # ---------------------------------------------------------------------- 110 | # __main__ .... code section 111 | 112 | if __name__ == "__main__": 113 | """Optionally... 114 | : - print the script source name. 115 | : - run the _demo 116 | """ 117 | # print("Script... {}".format(script)) 118 | # pnts, mesh = _demo() -------------------------------------------------------------------------------- /TableTools/Scripts/poly_field_calcs.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | poly_field_calcs 4 | ================ 5 | 6 | Script : poly_field_calcs.py 7 | 8 | Author : Dan_Patterson@carleton.ca 9 | 10 | Modified : 2018-07-25 11 | 12 | Purpose : tools for working ArcGIS Pro field calculator 13 | 14 | Useage: 15 | ------- 16 | 17 | 18 | **Requires** 19 | ------------ 20 | see import section and __init__.py in the `arraytools` folder 21 | 22 | **Notes** 23 | --------- 24 | 25 | **Basic array information** 26 | 27 | """ 28 | # ---- imports, formats, constants ------------------------------------------- 29 | import sys 30 | import inspect 31 | from textwrap import dedent, indent 32 | import numpy as np 33 | import arcpy 34 | 35 | arcpy.env.overwriteOutput = True 36 | 37 | script = sys.argv[0] 38 | 39 | # ---- simple functions ------------------------------------------------------ 40 | 41 | __all__ = [] 42 | 43 | if len(sys.argv) == 1: 44 | testing = True 45 | create_output = False 46 | in_tbl = "/shapes_mtm9" 47 | gdb = "/Table_tools.gdb" 48 | flder = "/".join(script.split("/")[:-2]) 49 | wrkspace = flder + gdb 50 | in_tbl = wrkspace + in_tbl 51 | in_fld = None 52 | else: 53 | testing = False 54 | create_output = True 55 | in_tbl = sys.argv[1] 56 | in_fld = sys.argv[2] 57 | exp_key = sys.argv[3] 58 | 59 | # ---- Do the work ----------------------------------------------------------- 60 | # 61 | desc = arcpy.da.Describe(in_tbl) 62 | wrkspace = desc['path'] 63 | 64 | # ---- Expression functions 65 | fld_name = in_fld 66 | if exp_key == "area sq km": 67 | args = ["Area_sqkm", 68 | "!Shape!.getArea('GEODESIC','SQUAREKILOMETERS')", "#"] 69 | elif exp_key == "leng km": 70 | args = ["Leng_km", 71 | "!Shape!.getLength('GEODESIC','KILOMETERS')", "#"] 72 | elif exp_key in ("sum angles", "min angle", "max angle"): 73 | from geometry import angles_ 74 | if inspect.isfunction(angles_): 75 | lines, ln_num = inspect.getsourcelines(angles_) 76 | code = "".join(["{}".format(line) for line in lines]) 77 | if exp_key == "sum angles": 78 | fld_expr = "angles_(!Shape!, kind='sum')" 79 | fld_name = "Angle_sum" 80 | elif exp_key == "min angle": 81 | fld_expr = "angles_(!Shape!, kind='min')" 82 | fld_name = "Angle_min" 83 | elif exp_key == "max angle": 84 | fld_expr = "angles_(!Shape!, kind='max')" 85 | fld_name = "Angle_max" 86 | args = [fld_name, fld_expr, code] 87 | elif exp_key in ("min length", "max length", "avg length"): 88 | from geometry import lengths_ 89 | if inspect.isfunction(lengths_): 90 | lines, ln_num = inspect.getsourcelines(lengths_) 91 | code = "".join(["{}".format(line) for line in lines]) 92 | if exp_key == "avg length": 93 | fld_expr = "lengths_(!Shape!, kind='avg')" 94 | fld_name = "Length_avg" 95 | elif exp_key == "min length": 96 | fld_expr = "lengths_(!Shape!, kind='min')" 97 | fld_name = "Length_min" 98 | elif exp_key == "max length": 99 | fld_expr = "lengths_(!Shape!, kind='max')" 100 | fld_name = "Length_max" 101 | args = [fld_name, fld_expr, code] 102 | 103 | fld_name, fld_expr, code = args 104 | 105 | arcpy.MakeTableView_management( 106 | in_table=in_tbl, 107 | out_view="tbl_view", 108 | workspace=wrkspace) 109 | 110 | if in_fld in (None, "", " "): 111 | fld_name = fld_name 112 | else: 113 | fld_name = in_fld 114 | fld_name = arcpy.ValidateFieldName(fld_name) 115 | arcpy.AddField_management( 116 | "tbl_view", 117 | field_name=fld_name, 118 | field_type="DOUBLE", 119 | field_is_nullable="NULLABLE") 120 | 121 | arcpy.CalculateField_management( 122 | in_table="tbl_view", 123 | field=fld_name, 124 | expression=fld_expr, 125 | code_block=code) 126 | 127 | del in_fld, in_tbl, arcpy 128 | # ---------------------------------------------------------------------- 129 | # __main__ .... code section 130 | 131 | if __name__ == "__main__": 132 | """Optionally... 133 | : - print the script source name. 134 | : - run the _demo 135 | """ 136 | # print("Script... {}".format(script)) 137 | # pnts, mesh = _demo() -------------------------------------------------------------------------------- /TableTools/Scripts/rank_flds.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | """ 3 | :Script: rank_flds.py 4 | :Author: Dan.Patterson@carleton.ca 5 | :Modified: 2017-11-01 6 | :Purpose: tools for working with numpy arrays 7 | :Useage: Runk the toolbox, select the parameters 8 | : 9 | :References: sure are 10 | : 11 | :---------------------------------------------------------------------: 12 | """ 13 | # ---- imports, formats, constants ---- 14 | import sys 15 | import numpy as np 16 | import arcpy 17 | 18 | 19 | ft = {'bool': lambda x: repr(x.astype('int32')), 20 | 'float': '{: 0.3f}'.format} 21 | 22 | np.set_printoptions(edgeitems=10, linewidth=80, precision=2, suppress=True, 23 | threshold=100, formatter=ft) 24 | np.ma.masked_print_option.set_display('-') # change to a single - 25 | 26 | script = sys.argv[0] # print this should you need to locate the script 27 | 28 | 29 | def tweet(msg): 30 | """Print a message for both arcpy and python. 31 | : msg - a text message 32 | """ 33 | m = "\n{}\n".format(msg) 34 | arcpy.AddMessage(m) 35 | print(m) 36 | print(arcpy.GetMessages()) 37 | 38 | 39 | in_tbl = sys.argv[1] 40 | in_flds = sys.argv[2] 41 | out_fld = sys.argv[3] 42 | 43 | if ';' in in_flds: 44 | in_flds = in_flds.split(';') 45 | else: 46 | in_flds = [in_flds] 47 | 48 | desc = arcpy.da.Describe(in_tbl) 49 | tbl_path = desc['path'] 50 | fnames = [i.name for i in arcpy.ListFields(in_tbl)] 51 | if out_fld in fnames: 52 | out_fld += 'dup' 53 | out_fld = arcpy.ValidateFieldName(out_fld, tbl_path) 54 | args = [in_tbl, in_flds, out_fld, tbl_path] 55 | msg = "in_tbl {}\nin_fld {}\nout_fld {}\ntbl_path {}".format(*args) 56 | tweet(msg) 57 | oid = 'OBJECTID' 58 | vals = [oid] + in_flds 59 | arr = arcpy.da.TableToNumPyArray(in_tbl, vals) 60 | tweet("{!r:}".format(arr)) 61 | for v in vals: 62 | fix_null = np.where(arr[v] == 'None', '', arr[v]) 63 | arr[v] = fix_null 64 | arr_sort = np.sort(arr, order=in_flds) 65 | dt = [(oid, ' 2 | 3 | 4 | 5 | 6 | 7 | {% seo %} 8 | 9 | 10 | 11 | 14 | 15 | 16 |
17 |
18 |

{{ site.title | default: site.github.repository_name }}

19 |

{{ site.description | default: site.github.project_tagline }}

20 | 21 | 28 | 29 | {% if site.github.is_project_page %} 30 |

Maintained by {{ site.github.owner_name }}

31 | {% endif %} 32 | 33 | {% if site.github.is_user_page %} 34 | 37 | {% endif %} 38 |
39 | 40 |
41 | {{ content }} 42 |
43 | 44 | 47 |
48 | 49 | {% if site.google_analytics %} 50 | 58 | {% endif %} 59 | 60 | 61 | -------------------------------------------------------------------------------- /arraytools_testing/ArrayTools.tbx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dan-Patterson/Tools_for_ArcGIS_Pro/6315d33469107b96c691a71b52ec695045a388c1/arraytools_testing/ArrayTools.tbx -------------------------------------------------------------------------------- /arraytools_testing/ReadMe.md: -------------------------------------------------------------------------------- 1 | **arraytools_testing** 2 | 3 | to fill 4 | -------------------------------------------------------------------------------- /arraytools_testing/array_tools_testing.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | array_tools_testing 4 | =================== 5 | 6 | Script : array_tools_testing.py 7 | 8 | Author : Dan_Patterson@carleton.ca 9 | 10 | Modified : 2018-09-19 11 | 12 | Purpose: tools for working with numpy arrays 13 | 14 | Useage : 15 | 16 | References 17 | ---------- 18 | ``_. 19 | ``_. 20 | --------------------------------------------------------------------- 21 | """ 22 | 23 | import sys 24 | import numpy as np 25 | #from arraytools.fc_tools._common import fc_info, fld_info 26 | import arcpy 27 | 28 | 29 | ft = {'bool': lambda x: repr(x.astype(np.int32)), 30 | 'float_kind': '{: 0.3f}'.format} 31 | np.set_printoptions(edgeitems=5, linewidth=80, precision=2, suppress=True, 32 | threshold=100, formatter=ft) 33 | np.ma.masked_print_option.set_display('-') # change to a single - 34 | 35 | script = sys.argv[0] # print this should you need to locate the script 36 | 37 | 38 | def id_geom(in_fc, as_pnts=True): 39 | """The main code segment which gets the id and shape information and 40 | explodes the geometry to individual points 41 | """ 42 | with arcpy.da.SearchCursor(in_fc, ['OBJECTID', 'SHAPE@']) as cursor: 43 | a = [[row[0], row[1]] for row in cursor] 44 | return a 45 | 46 | 47 | def pip_arc(pnt_fc, poly_fc): 48 | """Naive point-in-polygon using arcpy 49 | 50 | pnt_fc : PointGeometry 51 | PointGeometry only supports `within` geometry operations 52 | poly_fc : Polygon 53 | Polgon geometry 54 | 55 | Notes: 56 | ------ 57 | `within` options are `BOUNDARY`, `CLEMENTINI`, `PROPER`. 58 | - `BOUNDARY` on boundary 59 | - `CLEMENTINI` default, must be within, not on boundary 60 | """ 61 | pnts = id_geom(pnt_fc) 62 | polys = id_geom(poly_fc) 63 | out = [] 64 | for pnt in pnts: 65 | pid, p = pnt 66 | for poly in polys: 67 | plid, pol = poly 68 | if p.within(pol, "CLEMENTINI"): # CLEMENTINI, PROPER 69 | out.append([pid, plid]) #[pid, p, plid, pol]) 70 | break 71 | # else: 72 | # continue 73 | return out #pnts, polys, out 74 | 75 | 76 | def extend_tbl(arr, in_fc=None, join_id="PntID", col_id="PolyID"): 77 | """ExtendTable example 78 | """ 79 | dt = [(join_id, '`_. 17 | 18 | Derived from python snippet output... 19 | 20 | >>> in_arr = 'C:/Temp/x.npy' 21 | >>> out_gdb = 'C:/GIS/Tools_scripts/Table_tools/Table_tools.gdb' 22 | >>> out_name = 'sample_1000_npy' 23 | >>> arcpy.Tabletools.NumPyArrayToTable(in_arr, out_gdb, out_name) 24 | 25 | --------------------------------------------------------------------- 26 | """ 27 | # ---- imports, formats, constants ---- 28 | import sys 29 | from textwrap import dedent 30 | import numpy as np 31 | from arcpy import (AddMessage, ListTables, ValidateTableName, 32 | MakeTableView_management) 33 | from arcpy.da import NumPyArrayToTable 34 | from arcpy.geoprocessing import env 35 | 36 | ft = {'bool': lambda x: repr(x.astype(np.int32)), 37 | 'float_kind': '{: 0.3f}'.format} 38 | np.set_printoptions(edgeitems=5, linewidth=80, precision=2, suppress=True, 39 | threshold=100, formatter=ft) 40 | np.ma.masked_print_option.set_display('-') # change to a single - 41 | 42 | script = sys.argv[0] # print this should you need to locate the script 43 | 44 | 45 | def tweet(msg): 46 | """Print a message for both arcpy and python. 47 | : msg - a text message 48 | """ 49 | m = "\n{}\n".format(msg) 50 | AddMessage(m) 51 | print(m) 52 | 53 | 54 | # ---------------------------------------------------------------------- 55 | # .... final code section producing the featureclass and extendtable 56 | if len(sys.argv) == 1: 57 | testing = True 58 | pth = script.split("/")[:-1] 59 | pth = "/".join(pth) + "/Data/sample_20.npy" 60 | a = np.load(pth) 61 | frmt = "Result...\n{}" 62 | print(frmt.format(a)) 63 | else: 64 | testing = False 65 | in_arr = sys.argv[1] 66 | out_name = sys.argv[2] 67 | out_gdb = sys.argv[3] 68 | make_tbl_view = sys.argv[4] 69 | env.workspace = out_gdb 70 | tbls = ListTables() 71 | out_name = ValidateTableName(out_name) 72 | if tbls is not None: 73 | if out_name in tbls: 74 | out_name += '_dup' 75 | out_tbl = out_gdb + "/" + out_name 76 | # ---- call section for processing function 77 | # 78 | a = np.load(in_arr) 79 | NumPyArrayToTable(a, out_tbl) # create the table 80 | if make_tbl_view in (True, 'True', 1): 81 | MakeTableView_management(out_tbl, out_name) 82 | args = [in_arr, out_gdb, out_name] 83 | msg = """ 84 | :------------------------------------------------------------ 85 | 86 | Input array... {} 87 | Output gdb.... {} 88 | Output name... {} 89 | 90 | Conversion complete... 91 | Add the table manually if you want to see it... 92 | 93 | You need to refresh the geodatabase first since there is no 94 | autorefresh 95 | 96 | :------------------------------------------------------------ 97 | """ 98 | msg = dedent(msg).format(*args) 99 | tweet(msg) 100 | 101 | # 102 | if not testing: 103 | tweet('\nConversion done...') 104 | 105 | 106 | # ---------------------------------------------------------------------- 107 | # __main__ .... code section 108 | if __name__ == "__main__": 109 | """Optionally... 110 | : - print the script source name. 111 | : - run the _demo 112 | """ 113 | # print("Script... {}".format(script)) 114 | -------------------------------------------------------------------------------- /arraytools_testing/npy2ras.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | npy2ras 4 | ======= 5 | 6 | Script : npy2ras.py 7 | 8 | Author : Dan_Patterson@carleton.ca 9 | 10 | Modified : 2018-09-26 11 | 12 | Purpose : tools for working with numpy arrays 13 | 14 | References 15 | ---------- 16 | ``_. 18 | ``_. 19 | ``_. 20 | --------------------------------------------------------------------- 21 | """ 22 | 23 | import sys 24 | import numpy as np 25 | from arcpy import Point 26 | from arcgisscripting import NumPyArrayToRaster 27 | from arcpy import env 28 | 29 | env.overwriteOutput = True 30 | 31 | #from arcpytools import fc_info, tweet #, frmt_rec, _col_format 32 | 33 | ft = {'bool': lambda x: repr(x.astype(np.int32)), 34 | 'float_kind': '{: 0.3f}'.format} 35 | np.set_printoptions(edgeitems=10, linewidth=80, precision=2, suppress=True, 36 | threshold=100, formatter=ft) 37 | np.ma.masked_print_option.set_display('-') # change to a single - 38 | 39 | script = sys.argv[0] # print this should you need to locate the script 40 | 41 | # ---------------------------------------------------------------------- 42 | # .... final code section producing the featureclass and extendtable 43 | if len(sys.argv) == 1: 44 | testing = True 45 | pth = script.split("/")[:-1] 46 | pth0 = "/".join(pth) + "/Data/r00.npy" 47 | LL_x = 300000. 48 | LL_y = 5030000. 49 | cell_sze =10. 50 | no_data = 255 51 | in_arr = np.load(pth0) 52 | pth1 = "/".join(pth) + "/Data/r01.tif" 53 | # parameters here 54 | else: 55 | testing = False 56 | pth0 = sys.argv[1] 57 | LL_x = float(sys.argv[2]) 58 | LL_y = float(sys.argv[3]) 59 | cell_sze = float(sys.argv[4]) 60 | pth1 = sys.argv[5] 61 | if pth1[-4:] != ".tif": 62 | pth1 += ".tif" 63 | in_arr = np.load(pth0) 64 | # parameters here 65 | # 66 | to_pro = True # ---- change to True to produce tif for ArcGIS PRO 67 | 68 | dt_kind = in_arr.dtype.kind 69 | if dt_kind in ('u', 'i'): 70 | no_data = np.iinfo(in_arr.dtype.str).max 71 | elif dt_kind in ('f'): 72 | no_data = np.iinfo(in_arr.dtype.str).max 73 | else: 74 | no_data = None 75 | if to_pro: 76 | ras = NumPyArrayToRaster(in_arr, 77 | lower_left_corner=Point(LL_x, LL_y), 78 | x_cell_size=cell_sze, 79 | value_to_nodata=no_data 80 | ) 81 | ras.save(pth1) 82 | 83 | if testing: 84 | print('\nScript source... {}'.format(script)) 85 | # ---------------------------------------------------------------------- 86 | # __main__ .... code section 87 | if __name__ == "__main__": 88 | """Optionally... 89 | : - print the script source name. 90 | : - run the _demo 91 | """ 92 | -------------------------------------------------------------------------------- /arraytools_testing/ras2npy.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | ras2npy.py 4 | ========== 5 | 6 | Script : ras2npy.py # raster to numpy array as *.npy file 7 | 8 | Author : Dan_Patterson@carleton.ca 9 | 10 | Modified : 2018-09-25 11 | 12 | Purpose: tools for working with numpy arrays 13 | 14 | Useage : 15 | 16 | References 17 | ---------- 18 | ``_. 20 | ``_. 21 | ``_. 22 | --------------------------------------------------------------------- 23 | """ 24 | 25 | import sys 26 | import os 27 | from textwrap import dedent 28 | import numpy as np 29 | from art_common import (tweet, rasterfile_info) 30 | from arcpy import Point, Raster, RasterToNumPyArray 31 | from arcpy import env 32 | 33 | ft = {'bool': lambda x: repr(x.astype(np.int32)), 34 | 'float_kind': '{: 0.3f}'.format} 35 | np.set_printoptions(edgeitems=5, linewidth=80, precision=2, suppress=True, 36 | threshold=100, formatter=ft) 37 | np.ma.masked_print_option.set_display('-') # change to a single - 38 | 39 | script = sys.argv[0] # print this should you need to locate the script 40 | 41 | env.overwriteOutput = True 42 | 43 | # ---------------------------------------------------------------------- 44 | # .... final code section producing the featureclass and extendtable 45 | if len(sys.argv) == 1: 46 | testing = True 47 | pth = script.split("/")[:-1] 48 | pth0 = "/".join(pth) + "/Data/r00.tif" 49 | r = Raster(pth0) 50 | out_arr = "/".join(pth) + "/Data/r01.npy" 51 | frmt = "Result...\n{}" 52 | # print(frmt.format(a)) 53 | else: 54 | testing = False 55 | pth = sys.argv[1] 56 | out_arr = sys.argv[2] 57 | r = Raster(pth) 58 | # parameters here 59 | LL = r.extent.lowerLeft 60 | cols = int(r.extent.width/r.meanCellWidth) 61 | rows = int(r.extent.height/r.meanCellWidth) 62 | a = RasterToNumPyArray(r, 63 | lower_left_corner=Point(LL.X, LL.Y), 64 | ncols=cols, 65 | nrows=rows, 66 | nodata_to_value=r.noDataValue 67 | ) 68 | # 69 | # ---- overwrite existing outputs 70 | if os.path.isfile(out_arr): 71 | tweet("\nRemoving ... {}\nbefore saving".format(out_arr)) 72 | os.remove(out_arr) 73 | np.save(out_arr, a) 74 | if testing: 75 | tweet('\nScript source... {}'.format(script)) 76 | print('\nCleaning up') 77 | del r, tweet, rasterfile_info, Point, Raster, RasterToNumPyArray 78 | # ---------------------------------------------------------------------- 79 | # __main__ .... code section 80 | if __name__ == "__main__": 81 | """Optionally... 82 | : - print the script source name. 83 | : - run the _demo 84 | """ 85 | -------------------------------------------------------------------------------- /arraytools_testing/tbl2np.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | tbl2np 4 | ====== 5 | 6 | Script : tbl2np.py 7 | 8 | Author : Dan_Patterson@carleton.ca 9 | 10 | Modified : 2018-09-24 11 | 12 | Purpose: tools for working with numpy arrays 13 | 14 | Useage : 15 | 16 | References 17 | ---------- 18 | ``_. 19 | ``_. 20 | --------------------------------------------------------------------- 21 | """ 22 | 23 | import sys 24 | from textwrap import dedent 25 | import numpy as np 26 | from art_common import (tweet, de_punc, _describe, fc_info, fld_info, 27 | null_dict, tbl_arr) 28 | from arcpy.da import Describe 29 | 30 | ft = {'bool': lambda x: repr(x.astype(np.int32)), 31 | 'float_kind': '{: 0.3f}'.format} 32 | np.set_printoptions(edgeitems=5, linewidth=80, precision=2, suppress=True, 33 | threshold=100, formatter=ft) 34 | np.ma.masked_print_option.set_display('-') # change to a single - 35 | 36 | script = sys.argv[0] # print this should you need to locate the script 37 | 38 | # ---------------------------------------------------------------------- 39 | # .... final code section producing the featureclass and extendtable 40 | 41 | if len(sys.argv) == 1: 42 | testing = True 43 | pth = script.split("/")[:-1] 44 | pth = "/".join(pth) + "/array_tools.gdb/pnts_2000" 45 | a = tbl_arr(pth) 46 | frmt = "Result...\n{}" 47 | print(frmt.format(a)) 48 | else: 49 | testing = False 50 | in_tbl = sys.argv[1] 51 | desc = Describe(in_tbl) 52 | pth = desc['catalogPath'] 53 | # out_folder = sys.argv[2] 54 | # out_name = sys.argv[3] 55 | out_arr = sys.argv[2] # + "/" + out_name 56 | # ---- call section for processing function 57 | # 58 | a = tbl_arr(pth) 59 | np.save(out_arr, a) 60 | args = [a, out_arr] 61 | msg = """ 62 | :------------------------------------------------------------ 63 | 64 | Input table... {} 65 | Output array.... {} 66 | 67 | Conversion complete... 68 | You can reload the array using np.load(drive:/path/name.py) 69 | 70 | :------------------------------------------------------------ 71 | """ 72 | msg = dedent(msg).format(*args) 73 | tweet(msg) 74 | if testing: 75 | print('\nScript source... {}'.format(script)) 76 | # ---------------------------------------------------------------------- 77 | # __main__ .... code section 78 | if __name__ == "__main__": 79 | """Optionally... 80 | : - print the script source name. 81 | : - run the _demo 82 | """ 83 | -------------------------------------------------------------------------------- /assets/css/style.scss: -------------------------------------------------------------------------------- 1 | --- 2 | --- 3 | 4 | @import "{{ site.theme }}"; 5 | /* Wrapper change from 960px */ 6 | .wrapper { 7 | width:1280px; 8 | } 9 | 10 | /* Code blocks */ 11 | 12 | code, pre { 13 | font-family: Monaco, "Bitstream Vera Sans Mono", "Lucida Console", Terminal, monospace; 14 | color:#000; 15 | font-size:12px; 16 | } 17 | 18 | pre { 19 | padding: 4px 12px; 20 | background: #FDFEFB; 21 | border-radius:4px; 22 | border:1px solid #D7D8C8; 23 | overflow: auto; 24 | overflow-y: hidden; 25 | margin-bottom: 32px; 26 | } 27 | 28 | /* Section - for main page content changed from 650*/ 29 | 30 | section { 31 | width:960px; 32 | float:right; 33 | padding-bottom:50px; 34 | } 35 | 36 | 37 | /* Footer */ 38 | 39 | footer { 40 | width:170px; 41 | float:left; 42 | position:fixed; 43 | bottom:10px; 44 | padding-left: 50px; 45 | } 46 | 47 | /* changed max width from 960px */ 48 | @media print, screen and (max-width: 1280px) { 49 | 50 | div.wrapper { 51 | width:auto; 52 | margin:0; 53 | } 54 | 55 | header, section, footer { 56 | float:none; 57 | position:static; 58 | width:auto; 59 | } 60 | 61 | footer { 62 | border-top: 1px solid #ccc; 63 | margin:0 84px 0 50px; 64 | padding:0; 65 | } 66 | 67 | header { 68 | padding-right:320px; 69 | } 70 | 71 | section { 72 | padding:20px 84px 20px 50px; 73 | margin:0 0 20px; 74 | } 75 | 76 | header a small { 77 | display:inline; 78 | } 79 | 80 | header ul { 81 | position:absolute; 82 | right:130px; 83 | top:84px; 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /concavehull/Concave_hull.tbx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dan-Patterson/Tools_for_ArcGIS_Pro/6315d33469107b96c691a71b52ec695045a388c1/concavehull/Concave_hull.tbx -------------------------------------------------------------------------------- /concavehull/Data/concave_hull.gdb/a00000001.TablesByName.atx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dan-Patterson/Tools_for_ArcGIS_Pro/6315d33469107b96c691a71b52ec695045a388c1/concavehull/Data/concave_hull.gdb/a00000001.TablesByName.atx -------------------------------------------------------------------------------- /concavehull/Data/concave_hull.gdb/a00000001.gdbindexes: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dan-Patterson/Tools_for_ArcGIS_Pro/6315d33469107b96c691a71b52ec695045a388c1/concavehull/Data/concave_hull.gdb/a00000001.gdbindexes -------------------------------------------------------------------------------- /concavehull/Data/concave_hull.gdb/a00000001.gdbtable: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dan-Patterson/Tools_for_ArcGIS_Pro/6315d33469107b96c691a71b52ec695045a388c1/concavehull/Data/concave_hull.gdb/a00000001.gdbtable -------------------------------------------------------------------------------- /concavehull/Data/concave_hull.gdb/a00000001.gdbtablx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dan-Patterson/Tools_for_ArcGIS_Pro/6315d33469107b96c691a71b52ec695045a388c1/concavehull/Data/concave_hull.gdb/a00000001.gdbtablx -------------------------------------------------------------------------------- /concavehull/Data/concave_hull.gdb/a00000002.gdbtable: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dan-Patterson/Tools_for_ArcGIS_Pro/6315d33469107b96c691a71b52ec695045a388c1/concavehull/Data/concave_hull.gdb/a00000002.gdbtable -------------------------------------------------------------------------------- /concavehull/Data/concave_hull.gdb/a00000002.gdbtablx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dan-Patterson/Tools_for_ArcGIS_Pro/6315d33469107b96c691a71b52ec695045a388c1/concavehull/Data/concave_hull.gdb/a00000002.gdbtablx -------------------------------------------------------------------------------- /concavehull/Data/concave_hull.gdb/a00000003.gdbindexes: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dan-Patterson/Tools_for_ArcGIS_Pro/6315d33469107b96c691a71b52ec695045a388c1/concavehull/Data/concave_hull.gdb/a00000003.gdbindexes -------------------------------------------------------------------------------- /concavehull/Data/concave_hull.gdb/a00000003.gdbtable: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dan-Patterson/Tools_for_ArcGIS_Pro/6315d33469107b96c691a71b52ec695045a388c1/concavehull/Data/concave_hull.gdb/a00000003.gdbtable -------------------------------------------------------------------------------- /concavehull/Data/concave_hull.gdb/a00000004.CatItemsByPhysicalName.atx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dan-Patterson/Tools_for_ArcGIS_Pro/6315d33469107b96c691a71b52ec695045a388c1/concavehull/Data/concave_hull.gdb/a00000004.CatItemsByPhysicalName.atx -------------------------------------------------------------------------------- /concavehull/Data/concave_hull.gdb/a00000004.CatItemsByType.atx: -------------------------------------------------------------------------------- 1 | {70737809-852C-4A03-9E22-2CECEA5B9BFA}{70737809-852C-4A03-9E22-2CECEA5B9BFA}{C673FE0F-7280-404F-8532-20755DD8FC06}{F3783E6F-65CA-4514-8315-CE3985DAD3B1}& -------------------------------------------------------------------------------- /concavehull/Data/concave_hull.gdb/a00000004.FDO_UUID.atx: -------------------------------------------------------------------------------- 1 | {29CC7A3D-EC54-4641-B913-5AEC23C28C7D}{30CDFD7D-0D6C-45A3-B41C-6DB6F3BC20A2}{5793674D-9483-4308-BAF6-1B65997D96DA}{B7468ED0-35A5-432F-AC96-9F9A92D3052F}& -------------------------------------------------------------------------------- /concavehull/Data/concave_hull.gdb/a00000004.gdbindexes: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dan-Patterson/Tools_for_ArcGIS_Pro/6315d33469107b96c691a71b52ec695045a388c1/concavehull/Data/concave_hull.gdb/a00000004.gdbindexes -------------------------------------------------------------------------------- /concavehull/Data/concave_hull.gdb/a00000004.gdbtable: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dan-Patterson/Tools_for_ArcGIS_Pro/6315d33469107b96c691a71b52ec695045a388c1/concavehull/Data/concave_hull.gdb/a00000004.gdbtable -------------------------------------------------------------------------------- /concavehull/Data/concave_hull.gdb/a00000004.gdbtablx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dan-Patterson/Tools_for_ArcGIS_Pro/6315d33469107b96c691a71b52ec695045a388c1/concavehull/Data/concave_hull.gdb/a00000004.gdbtablx -------------------------------------------------------------------------------- /concavehull/Data/concave_hull.gdb/a00000004.spx: -------------------------------------------------------------------------------- 1 | @ -------------------------------------------------------------------------------- /concavehull/Data/concave_hull.gdb/a00000005.CatItemTypesByName.atx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dan-Patterson/Tools_for_ArcGIS_Pro/6315d33469107b96c691a71b52ec695045a388c1/concavehull/Data/concave_hull.gdb/a00000005.CatItemTypesByName.atx -------------------------------------------------------------------------------- /concavehull/Data/concave_hull.gdb/a00000005.CatItemTypesByParentTypeID.atx: -------------------------------------------------------------------------------- 1 | & 2 | "#$&!%  {00000000-0000-0000-0000-000000000000}{28DA9E89-FF80-4D6D-8926-4EE2B161677D}{28DA9E89-FF80-4D6D-8926-4EE2B161677D}{28DA9E89-FF80-4D6D-8926-4EE2B161677D}{28DA9E89-FF80-4D6D-8926-4EE2B161677D}{28DA9E89-FF80-4D6D-8926-4EE2B161677D}{28DA9E89-FF80-4D6D-8926-4EE2B161677D}{28DA9E89-FF80-4D6D-8926-4EE2B161677D}{28DA9E89-FF80-4D6D-8926-4EE2B161677D}{28DA9E89-FF80-4D6D-8926-4EE2B161677D}{28DA9E89-FF80-4D6D-8926-4EE2B161677D}{28DA9E89-FF80-4D6D-8926-4EE2B161677D}{28DA9E89-FF80-4D6D-8926-4EE2B161677D}{28DA9E89-FF80-4D6D-8926-4EE2B161677D}{28DA9E89-FF80-4D6D-8926-4EE2B161677D}{28DA9E89-FF80-4D6D-8926-4EE2B161677D}{28DA9E89-FF80-4D6D-8926-4EE2B161677D}{28DA9E89-FF80-4D6D-8926-4EE2B161677D}{28DA9E89-FF80-4D6D-8926-4EE2B161677D}{28DA9E89-FF80-4D6D-8926-4EE2B161677D}{28DA9E89-FF80-4D6D-8926-4EE2B161677D}{77292603-930F-475D-AE4F-B8970F42F394}{77292603-930F-475D-AE4F-B8970F42F394}{77292603-930F-475D-AE4F-B8970F42F394}{77292603-930F-475D-AE4F-B8970F42F394}{77292603-930F-475D-AE4F-B8970F42F394}{77292603-930F-475D-AE4F-B8970F42F394}{77292603-930F-475D-AE4F-B8970F42F394}{77292603-930F-475D-AE4F-B8970F42F394}{8405ADD5-8DF8-4227-8FAC-3FCADE073386}{8405ADD5-8DF8-4227-8FAC-3FCADE073386}{8637F1ED-8C04-4866-A44A-1CB8288B3C63}{8637F1ED-8C04-4866-A44A-1CB8288B3C63}{D4912162-3413-476E-9DA4-2AEFBBC16939}{D4912162-3413-476E-9DA4-2AEFBBC16939}{D4912162-3413-476E-9DA4-2AEFBBC16939}{D4912162-3413-476E-9DA4-2AEFBBC16939}{FFD09C28-FE70-4E25-907C-AF8E8A5EC5F3}&& -------------------------------------------------------------------------------- /concavehull/Data/concave_hull.gdb/a00000005.CatItemTypesByUUID.atx: -------------------------------------------------------------------------------- 1 | &   & !#% "$ 2 | {28DA9E89-FF80-4D6D-8926-4EE2B161677D}{2D0BFE5D-66C0-4536-8534-B9C50771AAE4}{35B601F7-45CE-4AFF-ADB7-7702D3839B12}{37672BD2-B9F3-48C1-89B5-8C43BBBB6D57}{4945A015-D612-448D-AE82-24CEB89508AE}{4ED4A58E-621F-4043-95ED-850FBA45FCBC}{5B966567-FB87-4DDE-938B-B4B37423539D}{5ED667A3-9CA9-44A2-8029-D95BF23704B9}{70737809-852C-4A03-9E22-2CECEA5B9BFA}{73718A66-AFB9-4B88-A551-CFFA0AE12620}{74737149-DCB5-4257-8904-B9724E32A530}{76357537-3364-48AF-A4BE-783C7C28B5CB}{767152D3-ED66-4325-8774-420D46674E07}{77292603-930F-475D-AE4F-B8970F42F394}{7771FC7D-A38B-4FD3-8225-639D17E9A131}{787BEA35-4A86-494F-BB48-500B96145B58}{8405ADD5-8DF8-4227-8FAC-3FCADE073386}{8637F1ED-8C04-4866-A44A-1CB8288B3C63}{8C368B12-A12E-4C7E-9638-C9C64E69E98F}{A300008D-0CEA-4F6A-9DFA-46AF829A3DF2}{A3803369-5FC2-4963-BAE0-13EFFC09DD73}{B606A7E1-FA5B-439C-849C-6E9C2481537B}{C29DA988-8C3E-45F7-8B5C-18E51EE7BEB4}{C673FE0F-7280-404F-8532-20755DD8FC06}{CD06BC3B-789D-4C51-AAFA-A467912B8965}{D4912162-3413-476E-9DA4-2AEFBBC16939}{D86502F9-9758-45C6-9D23-6DD1A0107B47}{D98421EB-D582-4713-9484-43304D0810F6}{DB1B697A-3BB6-426A-98A2-6EE7A4C6AED3}{DC64B6E4-DC0F-43BD-B4F5-F22385DCF055}{DC9EF677-1AA3-45A7-8ACD-303A5202D0DC}{E6302665-416B-44FA-BE33-4E15916BA101}{EBEEE2C9-FA73-4BED-AC7D-AEE7D68AFC80}{F3783E6F-65CA-4514-8315-CE3985DAD3B1}{F51F4BF9-B699-4F7F-BF6A-62BB16B2ECC8}{F8413DCB-2248-4935-BFE9-315F397E5110}{FBDD7DD6-4A25-40B7-9A1A-ECC3D1172447}{FFD09C28-FE70-4E25-907C-AF8E8A5EC5F3}&& -------------------------------------------------------------------------------- /concavehull/Data/concave_hull.gdb/a00000005.gdbindexes: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dan-Patterson/Tools_for_ArcGIS_Pro/6315d33469107b96c691a71b52ec695045a388c1/concavehull/Data/concave_hull.gdb/a00000005.gdbindexes -------------------------------------------------------------------------------- /concavehull/Data/concave_hull.gdb/a00000005.gdbtable: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dan-Patterson/Tools_for_ArcGIS_Pro/6315d33469107b96c691a71b52ec695045a388c1/concavehull/Data/concave_hull.gdb/a00000005.gdbtable -------------------------------------------------------------------------------- /concavehull/Data/concave_hull.gdb/a00000005.gdbtablx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dan-Patterson/Tools_for_ArcGIS_Pro/6315d33469107b96c691a71b52ec695045a388c1/concavehull/Data/concave_hull.gdb/a00000005.gdbtablx -------------------------------------------------------------------------------- /concavehull/Data/concave_hull.gdb/a00000006.CatRelsByDestinationID.atx: -------------------------------------------------------------------------------- 1 | {5793674D-9483-4308-BAF6-1B65997D96DA}{B7468ED0-35A5-432F-AC96-9F9A92D3052F}& -------------------------------------------------------------------------------- /concavehull/Data/concave_hull.gdb/a00000006.CatRelsByOriginID.atx: -------------------------------------------------------------------------------- 1 | {29CC7A3D-EC54-4641-B913-5AEC23C28C7D}{29CC7A3D-EC54-4641-B913-5AEC23C28C7D}& -------------------------------------------------------------------------------- /concavehull/Data/concave_hull.gdb/a00000006.CatRelsByType.atx: -------------------------------------------------------------------------------- 1 | {DC78F1AB-34E4-43AC-BA47-1C4EABD0E7C7}{DC78F1AB-34E4-43AC-BA47-1C4EABD0E7C7}& -------------------------------------------------------------------------------- /concavehull/Data/concave_hull.gdb/a00000006.FDO_UUID.atx: -------------------------------------------------------------------------------- 1 | {0B84B30F-C336-4511-A14E-6F78EEF135B8}{D47D255E-AF19-4B4A-8424-9C3B3072F230}& -------------------------------------------------------------------------------- /concavehull/Data/concave_hull.gdb/a00000006.gdbindexes: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dan-Patterson/Tools_for_ArcGIS_Pro/6315d33469107b96c691a71b52ec695045a388c1/concavehull/Data/concave_hull.gdb/a00000006.gdbindexes -------------------------------------------------------------------------------- /concavehull/Data/concave_hull.gdb/a00000006.gdbtable: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dan-Patterson/Tools_for_ArcGIS_Pro/6315d33469107b96c691a71b52ec695045a388c1/concavehull/Data/concave_hull.gdb/a00000006.gdbtable -------------------------------------------------------------------------------- /concavehull/Data/concave_hull.gdb/a00000006.gdbtablx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dan-Patterson/Tools_for_ArcGIS_Pro/6315d33469107b96c691a71b52ec695045a388c1/concavehull/Data/concave_hull.gdb/a00000006.gdbtablx -------------------------------------------------------------------------------- /concavehull/Data/concave_hull.gdb/a00000007.CatRelTypesByBackwardLabel.atx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dan-Patterson/Tools_for_ArcGIS_Pro/6315d33469107b96c691a71b52ec695045a388c1/concavehull/Data/concave_hull.gdb/a00000007.CatRelTypesByBackwardLabel.atx -------------------------------------------------------------------------------- /concavehull/Data/concave_hull.gdb/a00000007.CatRelTypesByDestItemTypeID.atx: -------------------------------------------------------------------------------- 1 |      2 | {28DA9E89-FF80-4D6D-8926-4EE2B161677D}{28DA9E89-FF80-4D6D-8926-4EE2B161677D}{28DA9E89-FF80-4D6D-8926-4EE2B161677D}{28DA9E89-FF80-4D6D-8926-4EE2B161677D}{28DA9E89-FF80-4D6D-8926-4EE2B161677D}{70737809-852C-4A03-9E22-2CECEA5B9BFA}{70737809-852C-4A03-9E22-2CECEA5B9BFA}{70737809-852C-4A03-9E22-2CECEA5B9BFA}{70737809-852C-4A03-9E22-2CECEA5B9BFA}{70737809-852C-4A03-9E22-2CECEA5B9BFA}{70737809-852C-4A03-9E22-2CECEA5B9BFA}{70737809-852C-4A03-9E22-2CECEA5B9BFA}{8405ADD5-8DF8-4227-8FAC-3FCADE073386}{8637F1ED-8C04-4866-A44A-1CB8288B3C63}{A300008D-0CEA-4F6A-9DFA-46AF829A3DF2}{CD06BC3B-789D-4C51-AAFA-A467912B8965}{CD06BC3B-789D-4C51-AAFA-A467912B8965}{CD06BC3B-789D-4C51-AAFA-A467912B8965}{CD06BC3B-789D-4C51-AAFA-A467912B8965}{D86502F9-9758-45C6-9D23-6DD1A0107B47}{D98421EB-D582-4713-9484-43304D0810F6}{F3783E6F-65CA-4514-8315-CE3985DAD3B1}{F51F4BF9-B699-4F7F-BF6A-62BB16B2ECC8}& -------------------------------------------------------------------------------- /concavehull/Data/concave_hull.gdb/a00000007.CatRelTypesByForwardLabel.atx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dan-Patterson/Tools_for_ArcGIS_Pro/6315d33469107b96c691a71b52ec695045a388c1/concavehull/Data/concave_hull.gdb/a00000007.CatRelTypesByForwardLabel.atx -------------------------------------------------------------------------------- /concavehull/Data/concave_hull.gdb/a00000007.CatRelTypesByName.atx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dan-Patterson/Tools_for_ArcGIS_Pro/6315d33469107b96c691a71b52ec695045a388c1/concavehull/Data/concave_hull.gdb/a00000007.CatRelTypesByName.atx -------------------------------------------------------------------------------- /concavehull/Data/concave_hull.gdb/a00000007.CatRelTypesByOriginItemTypeID.atx: -------------------------------------------------------------------------------- 1 |  2 |     {28DA9E89-FF80-4D6D-8926-4EE2B161677D}{28DA9E89-FF80-4D6D-8926-4EE2B161677D}{28DA9E89-FF80-4D6D-8926-4EE2B161677D}{37672BD2-B9F3-48C1-89B5-8C43BBBB6D57}{37672BD2-B9F3-48C1-89B5-8C43BBBB6D57}{4ED4A58E-621F-4043-95ED-850FBA45FCBC}{5B966567-FB87-4DDE-938B-B4B37423539D}{70737809-852C-4A03-9E22-2CECEA5B9BFA}{73718A66-AFB9-4B88-A551-CFFA0AE12620}{74737149-DCB5-4257-8904-B9724E32A530}{76357537-3364-48AF-A4BE-783C7C28B5CB}{767152D3-ED66-4325-8774-420D46674E07}{7771FC7D-A38B-4FD3-8225-639D17E9A131}{7771FC7D-A38B-4FD3-8225-639D17E9A131}{A3803369-5FC2-4963-BAE0-13EFFC09DD73}{A3803369-5FC2-4963-BAE0-13EFFC09DD73}{D86502F9-9758-45C6-9D23-6DD1A0107B47}{D98421EB-D582-4713-9484-43304D0810F6}{EBEEE2C9-FA73-4BED-AC7D-AEE7D68AFC80}{EBEEE2C9-FA73-4BED-AC7D-AEE7D68AFC80}{F3783E6F-65CA-4514-8315-CE3985DAD3B1}{F3783E6F-65CA-4514-8315-CE3985DAD3B1}{F3783E6F-65CA-4514-8315-CE3985DAD3B1}& -------------------------------------------------------------------------------- /concavehull/Data/concave_hull.gdb/a00000007.CatRelTypesByUUID.atx: -------------------------------------------------------------------------------- 1 |     2 | {0D10B3A7-2F64-45E6-B7AC-2FC27BF2133C}{17E08ADB-2B31-4DCD-8FDD-DF529E88F843}{1BDF1154-1521-4B7D-98D0-73559F0EF3FB}{55D2F4DC-CB17-4E32-A8C7-47591E8C71DE}{583A5BAA-3551-41AE-8AA8-1185719F3889}{5DD0C1AF-CB3D-4FEA-8C51-CB3BA8D77CDB}{5F9085E0-788F-4354-AE3C-34C83A7EA784}{725BADAB-3452-491B-A795-55F32D67229C}{74DAC6C1-A6F2-4603-88A8-D09BCCFDCF21}{79CC71C8-B7D9-4141-9014-B6373E236ABB}{7D2A7A69-ABD8-4AA7-AC08-9E4DDB86289E}{8DB31AF1-DF7C-4632-AA10-3CC44B0C6914}{908A4670-1111-48C6-8269-134FDD3FE617}{95C22AFD-AA36-431D-B195-1779A256C6B2}{A1633A59-46BA-4448-8706-D8ABE2B2B02E}{B32B8563-0B96-4D32-92C4-086423AE9962}{CC28387C-441F-4D7C-A802-41A160317FE0}{CCD6E1C9-9238-40D4-843F-C5DAFD3D2BDE}{D022DE33-45BD-424C-88BF-5B1B6B957BD3}{D088B110-190B-4229-BDF7-89FDDD14D1EA}{DC739A70-9B71-41E8-868C-008CF46F16D7}{DC78F1AB-34E4-43AC-BA47-1C4EABD0E7C7}{E79B44E3-F833-4B12-90A1-364EC4DDC43E}& -------------------------------------------------------------------------------- /concavehull/Data/concave_hull.gdb/a00000007.gdbindexes: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dan-Patterson/Tools_for_ArcGIS_Pro/6315d33469107b96c691a71b52ec695045a388c1/concavehull/Data/concave_hull.gdb/a00000007.gdbindexes -------------------------------------------------------------------------------- /concavehull/Data/concave_hull.gdb/a00000007.gdbtable: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dan-Patterson/Tools_for_ArcGIS_Pro/6315d33469107b96c691a71b52ec695045a388c1/concavehull/Data/concave_hull.gdb/a00000007.gdbtable -------------------------------------------------------------------------------- /concavehull/Data/concave_hull.gdb/a00000007.gdbtablx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dan-Patterson/Tools_for_ArcGIS_Pro/6315d33469107b96c691a71b52ec695045a388c1/concavehull/Data/concave_hull.gdb/a00000007.gdbtablx -------------------------------------------------------------------------------- /concavehull/Data/concave_hull.gdb/a00000009.gdbindexes: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dan-Patterson/Tools_for_ArcGIS_Pro/6315d33469107b96c691a71b52ec695045a388c1/concavehull/Data/concave_hull.gdb/a00000009.gdbindexes -------------------------------------------------------------------------------- /concavehull/Data/concave_hull.gdb/a00000009.gdbtable: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dan-Patterson/Tools_for_ArcGIS_Pro/6315d33469107b96c691a71b52ec695045a388c1/concavehull/Data/concave_hull.gdb/a00000009.gdbtable -------------------------------------------------------------------------------- /concavehull/Data/concave_hull.gdb/a00000009.gdbtablx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dan-Patterson/Tools_for_ArcGIS_Pro/6315d33469107b96c691a71b52ec695045a388c1/concavehull/Data/concave_hull.gdb/a00000009.gdbtablx -------------------------------------------------------------------------------- /concavehull/Data/concave_hull.gdb/a00000009.spx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dan-Patterson/Tools_for_ArcGIS_Pro/6315d33469107b96c691a71b52ec695045a388c1/concavehull/Data/concave_hull.gdb/a00000009.spx -------------------------------------------------------------------------------- /concavehull/Data/concave_hull.gdb/a0000000a.gdbindexes: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dan-Patterson/Tools_for_ArcGIS_Pro/6315d33469107b96c691a71b52ec695045a388c1/concavehull/Data/concave_hull.gdb/a0000000a.gdbindexes -------------------------------------------------------------------------------- /concavehull/Data/concave_hull.gdb/a0000000a.gdbtable: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dan-Patterson/Tools_for_ArcGIS_Pro/6315d33469107b96c691a71b52ec695045a388c1/concavehull/Data/concave_hull.gdb/a0000000a.gdbtable -------------------------------------------------------------------------------- /concavehull/Data/concave_hull.gdb/a0000000a.spx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dan-Patterson/Tools_for_ArcGIS_Pro/6315d33469107b96c691a71b52ec695045a388c1/concavehull/Data/concave_hull.gdb/a0000000a.spx -------------------------------------------------------------------------------- /concavehull/Data/concave_hull.gdb/gdb: -------------------------------------------------------------------------------- 1 |  -------------------------------------------------------------------------------- /concavehull/Data/concave_hull.gdb/timestamps: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dan-Patterson/Tools_for_ArcGIS_Pro/6315d33469107b96c691a71b52ec695045a388c1/concavehull/Data/concave_hull.gdb/timestamps -------------------------------------------------------------------------------- /concavehull/README.md: -------------------------------------------------------------------------------- 1 | **Concave Hull** 2 | -------------------------------------------------------------------------------- /concavehull/concave_hull_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dan-Patterson/Tools_for_ArcGIS_Pro/6315d33469107b96c691a71b52ec695045a388c1/concavehull/concave_hull_icon.png -------------------------------------------------------------------------------- /field_calculator/Field_Calculator_main.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | field_calculator_tools 4 | ====================== 5 | 6 | Script : field_calculator_tools.py 7 | 8 | Author : Dan_Patterson@carleton.ca 9 | 10 | Modified : 2018-07-25 11 | 12 | Purpose : tools for working ArcGIS Pro field calculator 13 | 14 | Useage: 15 | ------- 16 | 17 | 18 | 19 | **Requires** 20 | ------------ 21 | see import section and __init__.py in the `arraytools` folder 22 | 23 | **Notes** 24 | --------- 25 | 26 | **Basic array information** 27 | 28 | """ 29 | # ---- imports, formats, constants ------------------------------------------- 30 | import sys 31 | import inspect 32 | from textwrap import dedent, indent 33 | import numpy as np 34 | import arcpy 35 | 36 | arcpy.env.overwriteOutput = True 37 | 38 | script = sys.argv[0] 39 | 40 | # ---- simple functions ------------------------------------------------------ 41 | 42 | __all__ = [] 43 | 44 | if len(sys.argv) == 1: 45 | testing = True 46 | create_output = False 47 | in_tbl = "/Carp_AOI" 48 | gdb = "/Field_calculator_tools.gdb" 49 | flder = "/".join(script.split("/")[:-2]) 50 | wrkspace = flder + gdb 51 | in_tbl = wrkspace + in_tbl 52 | in_fld = None 53 | else: 54 | testing = False 55 | create_output = True 56 | in_tbl = sys.argv[1] 57 | in_fld = sys.argv[2] 58 | exp_key = sys.argv[3] 59 | 60 | # ---- Do the work ----------------------------------------------------------- 61 | # 62 | desc = arcpy.da.Describe(in_tbl) 63 | wrkspace = desc['path'] 64 | 65 | # ---- Expression functions 66 | fld_name = in_fld 67 | if exp_key == "area sq km": 68 | args = ["Area_sqkm", "!Shape!.getArea('GEODESIC','SQUAREKILOMETERS')", 69 | "#"] 70 | elif exp_key == "leng km": 71 | args = ["Leng_km", "!Shape!.getLength('GEODESIC','KILOMETERS')", "#"] 72 | elif exp_key in ("sum angles", "min angle", "max angle"): 73 | from angles_ import angles_poly 74 | if inspect.isfunction(angles_poly): 75 | lines, ln_num = inspect.getsourcelines(angles_poly) 76 | code = "".join(["{}".format(line) for line in lines]) 77 | if exp_key == "sum angles": 78 | fld_expr = "angles_poly(!Shape!, kind='sum')" 79 | fld_name = "Angle_sum" 80 | elif exp_key == "min angle": 81 | fld_expr = "angles_poly(!Shape!, kind='min')" 82 | fld_name = "Angle_min" 83 | elif exp_key == "max angle": 84 | fld_expr = "angles_poly(!Shape!, kind='max')" 85 | fld_name = "Angle_max" 86 | args = [fld_name, fld_expr, code] 87 | elif exp_key in ("cumu_dist"): 88 | import cumu_dist 89 | from cumu_dist import dist_cumu 90 | if inspect.isfunction(dist_cumu): 91 | lines, ln_num = inspect.getsourcelines(dist_cumu) 92 | code = "".join(["{}".format(line) for line in lines]) 93 | if exp_key == "cumu_dist": 94 | fld_expr = "dist_cumu(!Shape!, is_first=True)" 95 | fld_name = "Cumu_dist" 96 | args = [fld_name, fld_expr, code] 97 | 98 | fld_name, fld_expr, code = args 99 | 100 | arcpy.MakeTableView_management( 101 | in_table=in_tbl, 102 | out_view="tbl_view", 103 | workspace=wrkspace) 104 | 105 | if in_fld in (None, "", " "): 106 | fld_name = fld_name 107 | else: 108 | fld_name = in_fld 109 | fld_name = arcpy.ValidateFieldName(fld_name) 110 | arcpy.AddField_management( 111 | "tbl_view", 112 | field_name=fld_name, 113 | field_type="DOUBLE", 114 | field_is_nullable="NULLABLE") 115 | 116 | arcpy.CalculateField_management( 117 | in_table="tbl_view", 118 | field=fld_name, 119 | expression=fld_expr, 120 | code_block=code) 121 | 122 | del in_fld, in_tbl, arcpy 123 | # ---------------------------------------------------------------------- 124 | # __main__ .... code section 125 | 126 | if __name__ == "__main__": 127 | """Optionally... 128 | : - print the script source name. 129 | : - run the _demo 130 | """ 131 | # print("Script... {}".format(script)) 132 | # pnts, mesh = _demo() -------------------------------------------------------------------------------- /field_calculator/angle_between.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | """----------------------------------------- 3 | Input shape field: returns angle between 0 and <360 based upon the first and last point 4 | azimuth_to(!Shape!,from_x, from_y, from_north=True) 5 | ie azimuth_to(!Shape!, 300050, 5000050, True) 6 | """ 7 | import math 8 | def azimuth_to(shape, from_x, from_y, from_north): 9 | x = shape.centroid.X 10 | y = shape.centroid.Y 11 | radian = math.atan2((y - from_y), (x - from_x)) 12 | angle = math.degrees(radian) 13 | if from_north: 14 | angle = (450 - angle) % 360 15 | return angle 16 | __esri_field_calculator_splitter__ 17 | azimuth_to(!Shape!, 300050, 5000050, True) -------------------------------------------------------------------------------- /field_calculator/angles_.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import numpy as np 3 | def angles_poly(a, inside=True, in_deg=True, kind="sum"): 4 | """Sequential angles from a poly* shape 5 | NOTE: this line.... angle = np.sum(angles) 6 | can be changed to `np.min`, `np.max` or others 7 | depending on what needs to be returned 8 | """ 9 | import numpy as np 10 | a = a.getPart() 11 | a =np.asarray([[i.X, i.Y] for j in a for i in j]) 12 | if len(a) < 2: 13 | return None 14 | elif len(a) == 2: # **** check 15 | ba = a[1] - a[0] 16 | return np.arctan2(*ba[::-1]) 17 | else: 18 | angles = [] 19 | if np.allclose(a[0], a[-1]): # closed loop 20 | a = a[:-1] 21 | r = (-1,) + tuple(range(len(a))) + (0,) 22 | else: 23 | r = tuple(range(len(a))) 24 | for i in range(len(r)-2): 25 | p0, p1, p2 = a[r[i]], a[r[i+1]], a[r[i+2]] 26 | ba = p1 - p0 27 | bc = p1 - p2 28 | cr = np.cross(ba, bc) 29 | dt = np.dot(ba, bc) 30 | ang = np.arctan2(np.linalg.norm(cr), dt) 31 | if not np.allclose(ang, np.pi): # check for extra vertices 32 | angles.append(ang) 33 | if in_deg: 34 | angles = np.degrees(angles) 35 | if kind == "sum": 36 | angle = np.sum(angles) 37 | elif kind == "min": 38 | angle = np.min(angles) 39 | elif kind == "max": 40 | angle = np.max(angles) 41 | return angle 42 | #__esri_field_calculator_splitter__ 43 | #angles_poly(!Shape!) -------------------------------------------------------------------------------- /field_calculator/azimuth_sequential_pnts.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | """ 3 | :Script: azimuth_sequential_pnts.py 4 | :Author: Dan.Patterson@carleton.ca 5 | :Modified: 2017-06-19 6 | :Purpose: tools for working with numpy arrays 7 | :Useage: 8 | : 9 | :References: 10 | : 11 | :---------------------------------------------------------------------: 12 | """ 13 | # ---- imports, formats, constants ---- 14 | x0 = 0.0 15 | y0 = 0.0 16 | angle = 0.0 17 | def angle_between(shape, from_north): 18 | """Calculate the angle/azimuth between sequential points in a point file. 19 | :Use: 20 | : .... angle_between(!Shape!, True) .... 21 | """ 22 | global x0 23 | global y0 24 | x = shape.centroid.X 25 | y = shape.centroid.Y 26 | if x0 == 0.0 and y0 == 0.0: 27 | x0 = x 28 | y0 = y 29 | return 0.0 30 | radian = math.atan2((y - y0), (x - x0)) 31 | angle = math.degrees(radian) 32 | if from_north: 33 | angle = (450 - angle) % 360 34 | x0 = x 35 | y0 = y 36 | return angle 37 | # __esri_field_calculator_splitter__ # optionally 38 | # angle_between(!Shape!, True) 39 | # 40 | # Python command... expr is the code block above 41 | # arcpy.management.CalculateField("poly_pnts", "Seq_angle", 42 | # "angle_between(!Shape!, True)", "PYTHON_9.3", 43 | # expr) 44 | # Angle_between(!Shape!) 45 | # ---------------------------------------------------------------------- 46 | # __main__ .... code section 47 | if __name__ == "__main__": 48 | """Optionally... 49 | : - print the script source name. 50 | : - run the _demo 51 | """ 52 | # print("Script... {}".format(script)) 53 | # _demo() 54 | 55 | from random import uniform 56 | def shift(val, start=-1, end=1): 57 | """shift within the range - start and end""" 58 | jiggle = uniform(start, end) 59 | return val + jiggle 60 | 61 | a = 10 62 | print(shift(10, -1, 1)) -------------------------------------------------------------------------------- /field_calculator/azimuth_to.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | """----------------------------------------- 3 | :Input shape field: 4 | :returns angle between 0 and <360 from the first and last point 5 | :azimuth_to(!Shape!,from_x, from_y, from_north=True) 6 | ie azimuth_to(!Shape!, 300050, 5000050, True) 7 | """ 8 | import math 9 | 10 | def azimuth_to(shape, from_x, from_y, from_north): 11 | x = shape.centroid.X 12 | y = shape.centroid.Y 13 | radian = math.atan2((y - from_y), (x - from_x)) 14 | angle = math.degrees(radian) 15 | if from_north: 16 | angle = (450 - angle) % 360 17 | return angle 18 | #__esri_field_calculator_splitter__ 19 | #azimuth_to(!Shape!, 300050, 5000050, True) -------------------------------------------------------------------------------- /field_calculator/cal_files/angle_between.cal: -------------------------------------------------------------------------------- 1 | x0 = 0.0 2 | y0 = 0.0 3 | angle = 0.0 4 | def angle_between(shape, from_north): 5 | """Calculate the angle/azimuth between sequential points in a point file. 6 | :Use: 7 | : .... angle_between(!Shape!, True) .... 8 | """ 9 | global x0 10 | global y0 11 | x = shape.centroid.X 12 | y = shape.centroid.Y 13 | if x0 == 0.0 and y0 == 0.0: 14 | x0 = x 15 | y0 = y 16 | return 0.0 17 | radian = math.atan2((y - y0), (x - x0)) 18 | angle = math.degrees(radian) 19 | if from_north: 20 | angle = (450 - angle) % 360 21 | x0 = x 22 | y0 = y 23 | return angle 24 | __esri_field_calculator_splitter__ 25 | angle_between(!Shape!, True) -------------------------------------------------------------------------------- /field_calculator/cal_files/angles.cal: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import arcpy 3 | def angles_poly(a, inside=True, in_deg=True): 4 | """Sequential angles from a poly* shape 5 | """ 6 | a = a.getPart() 7 | a =np.asarray([[i.X, i.Y] for j in a for i in j]) 8 | if len(a) < 2: 9 | return None 10 | elif len(a) == 2: # **** check 11 | ba = a[1] - a[0] 12 | return np.arctan2(*ba[::-1]) 13 | else: 14 | angles = [] 15 | if np.allclose(a[0], a[-1]): # closed loop 16 | a = a[:-1] 17 | r = (-1,) + tuple(range(len(a))) + (0,) 18 | else: 19 | r = tuple(range(len(a))) 20 | for i in range(len(r)-2): 21 | p0, p1, p2 = a[r[i]], a[r[i+1]], a[r[i+2]] 22 | ba = p1 - p0 23 | bc = p1 - p2 24 | cr = np.cross(ba, bc) 25 | dt = np.dot(ba, bc) 26 | ang = np.arctan2(np.linalg.norm(cr), dt) 27 | angles.append(ang) 28 | if in_deg: 29 | angles = np.degrees(angles) 30 | angle = np.sum(angles) 31 | return angle 32 | __esri_field_calculator_splitter__ 33 | angles_poly(!Shape!) -------------------------------------------------------------------------------- /field_calculator/cal_files/area_sqkm.cal: -------------------------------------------------------------------------------- 1 | !Shape!.getArea('GEODESIC','SQUAREKILOMETERS') -------------------------------------------------------------------------------- /field_calculator/cal_files/azimuth_to.cal: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | """----------------------------------------- 3 | Input shape field: returns angle between 0 and <360 based upon the first and last point 4 | azimuth_to(!Shape!,from_x, from_y, from_north=True) 5 | ie azimuth_to(!Shape!, 300050, 5000050, True) 6 | """ 7 | import math 8 | def azimuth_to(shape, from_x, from_y, from_north): 9 | x = shape.centroid.X 10 | y = shape.centroid.Y 11 | radian = math.atan2((y - from_y), (x - from_x)) 12 | angle = math.degrees(radian) 13 | if from_north: 14 | angle = (450 - angle) % 360 15 | return angle 16 | __esri_field_calculator_splitter__ 17 | azimuth_to(!Shape!, 300050, 5000050, True) -------------------------------------------------------------------------------- /field_calculator/cal_files/dist_to.cal: -------------------------------------------------------------------------------- 1 | """----------------------------------------- 2 | dist_to(shape, from_x, from_y) 3 | input: shape field, origin x,y 4 | returns: distance to the specified point 5 | expression: dist_to(!Shape!, x, y) 6 | """ 7 | def dist_to(shape, from_x, from_y): 8 | x = shape.centroid.X 9 | y = shape.centroid.Y 10 | distance = math.sqrt((x - from_x)**2 + (y - from_y)**2) 11 | return distance 12 | __esri_field_calculator_splitter__ 13 | dist_to(!Shape!, 1194453.15828, 986485.573819) -------------------------------------------------------------------------------- /field_calculator/cal_files/fld_calc_cursor.cal: -------------------------------------------------------------------------------- 1 | lst =[] 2 | in_tbl = r'C:\GIS\Tools_scripts\Table_tools\Table_tools.gdb\f1' 3 | fld_lst = ['sum_Pnts'] 4 | def fld_calc(in_fld): 5 | import arcpy 6 | global lst 7 | if len(lst) == 0: 8 | with arcpy.da.SearchCursor(in_tbl, fld_lst) as cursor: 9 | for row in cursor: 10 | lst.append(row[0]) 11 | del cursor, row 12 | # 13 | # Now do the work 14 | m = min(lst) 15 | ret = in_fld - m 16 | return ret 17 | __esri_field_calculator_splitter__ 18 | fld_calc(!sum_Pnts!) -------------------------------------------------------------------------------- /field_calculator/cal_files/index: -------------------------------------------------------------------------------- 1 | cal_file index 2 | 3 | Files with a *.cal extension are designed to be used in the field calculator in Pro (or arcmap) 4 | -------------------------------------------------------------------------------- /field_calculator/cal_files/length_km.cal: -------------------------------------------------------------------------------- 1 | !Shape!.getLength('GEODESIC','KILOMETERS') -------------------------------------------------------------------------------- /field_calculator/cal_files/natural_pad.cal: -------------------------------------------------------------------------------- 1 | import re 2 | def nat_pad(val, pad='0000'): 3 | """natural sort... put the import re outside of the function 4 | :if using the field calculator 5 | : calculator expression- nat_pad(!data_field!, pad='a bunch of 0s') 6 | """ 7 | txt = re.split('([0-9]+)', val) 8 | l_val = len(str(val)) 9 | txt_out = "{}{}{}".format(txt[0], pad[:-l_val], txt[1]) 10 | return txt_out 11 | if __name__ == '__main__': 12 | a = ['r1', 'r1', 'r1', 'r4', 'r4', 'r7', 'r7', 'r7', 'r10', 'r10'] 13 | print("input - \n{}".format(a)) 14 | vals = [nat_pad(i) for i in a] 15 | print("output - \n{}".format(vals)) -------------------------------------------------------------------------------- /field_calculator/cal_files/natural_sort.cal: -------------------------------------------------------------------------------- 1 | import re 2 | def natsort(lst): 3 | """natural sort""" 4 | import re 5 | convert = lambda text: int(text) if text.isdigit() else text 6 | a_key = lambda key: [convert(c) for c in re.split('([0-9]+)', key)] 7 | return sorted(lst, key=a_key) 8 | if __name__ == '__main__': 9 | a = ['r1', 'r1', 'r1', 'r4', 'r4', 'r7', 'r7', 'r7', 'r10', 'r10'] 10 | b = sorted(a) 11 | print("input - \n{}".format(a)) 12 | print("text sort - \n{}".format(b)) 13 | vals = natsort(a) 14 | print("natural sort - \n{}".format(vals)) -------------------------------------------------------------------------------- /field_calculator/cal_files/part_count.cal: -------------------------------------------------------------------------------- 1 | !Shape!.partCount -------------------------------------------------------------------------------- /field_calculator/cal_files/pnt_along.cal: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dan-Patterson/Tools_for_ArcGIS_Pro/6315d33469107b96c691a71b52ec695045a388c1/field_calculator/cal_files/pnt_along.cal -------------------------------------------------------------------------------- /field_calculator/cal_files/point_count.cal: -------------------------------------------------------------------------------- 1 | !Shape!.pointCount -------------------------------------------------------------------------------- /field_calculator/cal_files/rand_float.cal: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | def rand_float(N=1, begin=0, end=10, deci=2): 3 | """ Generate N random floats within the range begin - end 4 | :Technically, N random integers are produced then a random 5 | :amount within 0-1 is added to the value 6 | """ 7 | float_vals = np.random.randint(begin, end, size=(N)) 8 | float_vals = np.around(float_vals + np.random.rand(N), deci) 9 | return float_vals 10 | __esri_field_calculator_splitter__ 11 | rand_float() -------------------------------------------------------------------------------- /field_calculator/cal_files/rand_int.cal: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | def rand_int(low=1, high=10): 3 | """ Generate a random integers within the range low - high 4 | """ 5 | num = np.random.randint(low, high) 6 | return num 7 | __esri_field_calculator_splitter__ 8 | rand_int() -------------------------------------------------------------------------------- /field_calculator/cal_files/rand_norm.cal: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | def rand_norm(N=10, avg_=10, st_dev=1, deci=2): 3 | """ Generate N random floats within the range begin - end 4 | :Technically, N random integers are produced then a random 5 | :amount within 0-1 is added to the value 6 | """ 7 | float_vals = np.random.normal(avg_, st_dev) #, size=(N)) 8 | float_vals = np.around(float_vals + np.random.rand(), deci) #(N), deci) 9 | return float_vals 10 | __esri_field_calculator_splitter__ 11 | rand_norm() -------------------------------------------------------------------------------- /field_calculator/cal_files/seq_group.cal: -------------------------------------------------------------------------------- 1 | """Specify the number of records that you want to group in 'val' 2 | 'id' - a field containing sequential numbers or even random 3 | numbers. 4 | The number in 'id' is divided by 'val' and incremented if the 5 | modulus == 0 6 | """ 7 | cnt = 0 8 | def seq_group(id, val): 9 | global cnt 10 | if id % val == 0 : 11 | cnt += 1 12 | return cnt 13 | __esri_field_calculator_splitter__ 14 | seq_group(!OBJECTID!, 10) -------------------------------------------------------------------------------- /field_calculator/cal_files/seq_num_max.cal: -------------------------------------------------------------------------------- 1 | cnt = 0 2 | def seq_num_max(val): 3 | global cnt 4 | if cnt < val: 5 | cnt += 1 6 | else: 7 | cnt = 0 8 | return cnt 9 | __esri_field_calculator_splitter__ 10 | seq_num_max(3) -------------------------------------------------------------------------------- /field_calculator/cal_files/sequence.cal: -------------------------------------------------------------------------------- 1 | cnt = 0 2 | is_first = True 3 | def sequence(start, stop, step): 4 | global cnt, is_first 5 | if is_first: 6 | cnt = start 7 | is_first = False 8 | return cnt 9 | if cnt < stop: 10 | cnt += step 11 | else: 12 | cnt = start 13 | return cnt 14 | __esri_field_calculator_splitter__ 15 | sequence(2, 10, 2) -------------------------------------------------------------------------------- /field_calculator/cal_files/sequential_count.cal: -------------------------------------------------------------------------------- 1 | """ 2 | Count the number of 'val's that exist in a field and number and format 3 | them, incrementing the number each time one is found 4 | """ 5 | old = "" 6 | cnt = 0 7 | def seq_count(val): 8 | global old 9 | global cnt 10 | if old == val: 11 | cnt += 1 12 | ret = "{} {:04.0f}".format(val, cnt) 13 | else: 14 | cnt = 0 15 | ret = "{} {:04.0f}".format(val, cnt) 16 | old = val 17 | return ret 18 | __esri_field_calculator_splitter__ 19 | seq_count(!Test!) -------------------------------------------------------------------------------- /field_calculator/cal_files/sequential_id.cal: -------------------------------------------------------------------------------- 1 | cnt = 0 2 | def seq_count(val): 3 | global cnt 4 | if cnt >= val : 5 | cnt += 1 6 | return cnt 7 | 8 | __esri_field_calculator_splitter__ 9 | seq_count(0) -------------------------------------------------------------------------------- /field_calculator/coordinate_defs.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | """ 3 | :Script: coordinate_defs.py 4 | :Author: Dan.Patterson@carleton.ca 5 | :Modified: 2017-06-17 6 | :Purpose: convert string formatted coordinates in some variant of degrees 7 | : 8 | :---------------------------------------------------------------------: 9 | """ 10 | def dd_mm_ss(dd, cal_long=True, use_sign=False, use_quad=False): 11 | """decimal degrees to deg dec min""" 12 | deg_sign = u'\N{DEGREE SIGN}' 13 | deg = int(dd) 14 | if deg < 0: 15 | quad = ['S', 'W'][cal_long] 16 | deg = abs(deg) 17 | else: 18 | quad = ['N', 'E'][cal_long] 19 | if not use_quad: 20 | quad = "" 21 | if not use_sign: 22 | deg_sign = "" 23 | mins, secs = divmod(dd*3600, 60) 24 | degs, mins = divmod(mins, 60) 25 | frmt = "{}{}-{:0.0f}-{:05.2f}{}".format(deg, deg_sign, mins, secs, quad) 26 | return frmt 27 | 28 | 29 | def dd_dmm(dd, cal_long=True): 30 | """decimal degrees to deg dec min""" 31 | deg_sign = u'\N{DEGREE SIGN}' 32 | deg = int(dd) 33 | if deg < 0: 34 | quad = ['S', 'W'][cal_long] 35 | deg = abs(deg) 36 | else: 37 | quad = ['N', 'E'][cal_long] 38 | minsec = divmod((deg - dd)*60, 60)[-1] 39 | frmt = "{}{} {:0.2f}' {}".format(deg, deg_sign, minsec, quad) 40 | return frmt 41 | 42 | 43 | def ddm_ddd(a, sep=" "): 44 | """ convert degree, decimal minute string to decimal degrees 45 | : a - degree, decimal minute string 46 | : sep - usually a space, but check 47 | : Useage - ddm_ddd(!SourceField!, sep=" ") 48 | : python parser, sourcefield is the input string field, destination 49 | : field is type double 50 | """ 51 | d, m = [float(i) for i in a.split(sep)] 52 | sign = [-1, 1][d > 0] 53 | dd = sign*(abs(d) + m/60.) 54 | return dd 55 | 56 | 57 | def dms_ddd(a, sep=" "): 58 | """ convert degree, minute, decimal second string to decimal degrees 59 | : a - degree, minute, decimal second string 60 | : sep - usually a space, but check 61 | : Useage - dms_ddd(!SourceField!, sep=" ") 62 | : python parser, sourcefield is the input string field, destination 63 | : field is type double 64 | """ 65 | d, m, s = [float(i) for i in a.split(sep)] 66 | sign = [-1, 1][d > 0] 67 | dd = sign*(abs(d) + (m + s/60.)/60.) 68 | return dd 69 | # ---------------------------------------------------------------------- 70 | # __main__ .... code section 71 | if __name__ == "__main__": 72 | """Samples 73 | """ 74 | a = '45 30.30' 75 | b = '-75 45.45' 76 | c = '45 30 30.30' 77 | d = '-75 45 45.45' 78 | print('Input... {:>12s} to... {:> 12.8f}'.format(a, ddm_ddd(a, sep=" "))) 79 | print('Input... {:>12s} to... {:> 12.8f}'.format(b, ddm_ddd(b, sep=" "))) 80 | print('Input... {:>12s} to... {:> 12.8f}'.format(c, dms_ddd(c, sep=" "))) 81 | print('Input... {:>12s} to... {:> 12.8f}'.format(d, dms_ddd(d, sep=" "))) 82 | -------------------------------------------------------------------------------- /field_calculator/cumu_dist.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | def dist_cumu(shape, is_first=True): 3 | import arcpy 4 | global x0; global y0; global distance 5 | # 6 | arcpy.AddMessage(str(is_first)) 7 | if is_first: 8 | import math 9 | x0 = 0.0; y0 = 0.0; distance = 0.0 10 | x = shape.centroid.X; y = shape.centroid.Y 11 | # arcpy.AddMessage((str(x)+ str(y)) ) 12 | if x0 == 0.0 and y0 == 0.0: 13 | x0 = x; y0 = y 14 | distance += math.sqrt((x - x0)**2 + (y - y0)**2) 15 | x0 = x; y0 = y 16 | is_first = False 17 | arcpy.AddMessage(("{}, {} {}".format(x, y, distance) )) 18 | return distance, is_first -------------------------------------------------------------------------------- /field_calculator/date_time_defs.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | """ 3 | :Script: date_time_defs.py 4 | :Author: Dan.Patterson@carleton.ca 5 | :Modified: 2017-06-17 6 | :Purpose: date_time defs 7 | : Can be used in the field calculator in ArcMap or ArcGIS Pro 8 | :---------------------------------------------------------------------: 9 | """ 10 | from datetime import datetime 11 | 12 | # -*- coding: utf-8 -*- 13 | """ 14 | Script:  DDddd_DMS_convert.py 15 | Author:  Dan.Patterson@carleton.ca 16 | 17 | Purpose: Formatting stuff 18 | Notes: 19 | - to use in the field calculator, set the parser to Python and 20 |   use ' !fieldname! ' in the expression box 21 | - degree sign, ° ... ALT 248 on the numeric keypad 22 | - utf-8  u'\N{DEGREE SIGN}' or u'\xb0' 23 | """ 24 | 25 | def ddd_dms(a): 26 | """Decimal degree to DMS format""" 27 | sign = [-1, 1][a > 0] 28 | DD, dd = divmod(a, sign) 29 | MM, ss = divmod(dd*60, sign) 30 | SS, ssss = divmod(ss*60, 1) 31 | frmt = "{:0= 4}" + u'\xb0' + " {:=2}' {:0=7.4f}\" " 32 | DMS = frmt.format(int(sign*DD), int(MM), sign*ss*60) 33 | return DMS 34 | 35 | 36 | def get_date(fld): 37 | """input a date field, strip off the time and format 38 | :Useage - get_date(!FieldName!) 39 | :From - 2017-06-17 20:35:58.777353 ... 2017-06-17 40 | :Returns -2017-06-17 41 | """ 42 | if fld is not None: 43 | lst = [int(i) for i in (str(fld).split(" ")[0]).split("-")] 44 | return "{}-{:02.0f}-{:02.0f}".format(*lst) 45 | else: 46 | return None 47 | 48 | 49 | def get_time(fld): 50 | """input a date field, strip off the date and format 51 | :From - 2017-06-17 20:35:58.777353 ... 2017-06-17 52 | :Returns - 20 h 35 m 58.78 s 53 | """ 54 | if fld is not None: 55 | lst = [float(i) for i in (str(fld).split(" ")[1]).split(":")] 56 | return "{:02.0f} h {:02.0f} m {: 5.2f} s".format(*lst) 57 | else: 58 | return None 59 | 60 | def _demo(): 61 | """ 62 |     :other format options 63 |     :mess with the order of line 44 for different outputs 64 |     """ 65 | today = datetime.today() 66 | print('\n_demo def...\nISO     :', today) 67 | print('format(): {:%a %b %d %H:%M:%S %Y}'.format(today)) 68 | #return today 69 | 70 | #-------------------------------------------------------------------- 71 | # __main__ .... code section 72 | if __name__ == "__main__": 73 | """ A simple test """ 74 | n = str(datetime.now()) 75 | 76 | print("\nget_date def...\nDate from {} ... {}".format(n, get_date(n))) 77 | print("\nget_time def...\nDate from {} ... {}".format(n, get_time(n))) 78 | 79 | _demo() 80 | 81 | vals = [45.501234567890, -45.501234567890, 82 | 145.501234567890, -145.501234567890] 83 | print("\nddd_dms def...\nTest run with a in vals") 84 | for a in vals: 85 | print("{:> 22.12f} ... {!s:>20}".format(a, ddd_dms(a))) 86 | -------------------------------------------------------------------------------- /field_calculator/dist_to.py: -------------------------------------------------------------------------------- 1 | """----------------------------------------- 2 | dist_to(shape, from_x, from_y) 3 | input: shape field, origin x,y 4 | returns: distance to the specified point 5 | expression: dist_to(!Shape!, x, y) 6 | """ 7 | def dist_to(shape, from_x, from_y): 8 | x = shape.centroid.X 9 | y = shape.centroid.Y 10 | distance = math.sqrt((x - from_x)**2 + (y - from_y)**2) 11 | return distance 12 | __esri_field_calculator_splitter__ 13 | dist_to(!Shape!, 1194453.15828, 986485.573819) -------------------------------------------------------------------------------- /field_calculator/natural_pad.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | """ 3 | :Script: natural_pad.py 4 | :Author: Dan.Patterson@carleton.ca 5 | :Modified: 2017-08-23 6 | :Purpose: Returns a mixed text-number list accounting for numeric values 7 | : ['a1', 'a20', 'a2', 'a10'] should yield ['a001', 'a020', 'a002', 'a010'] 8 | """ 9 | 10 | import re 11 | 12 | 13 | def nat_pad(val, pad='0000'): 14 | """natural sort... put the import re outside of the function 15 | :if using the field calculator 16 | : calculator expression- nat_pad(!data_field!, pad='a bunch of 0s') 17 | """ 18 | txt = re.split('([0-9]+)', val) 19 | l_val = len(str(val)) 20 | txt_out = "{}{}{}".format(txt[0], pad[:-l_val], txt[1]) 21 | return txt_out 22 | 23 | 24 | # -------------------------------------------------------------------------- 25 | if __name__ == '__main__': 26 | a = ['a1', 'a20', 'a2', 'a10'] 27 | print("input - \n{}".format(a)) 28 | vals = [nat_pad(i) for i in a] 29 | print("output - \n{}".format(vals)) 30 | -------------------------------------------------------------------------------- /field_calculator/natural_sort.cal.py: -------------------------------------------------------------------------------- 1 | import re 2 | def natsort(lst): 3 | """natural sort""" 4 | import re 5 | convert = lambda text: int(text) if text.isdigit() else text 6 | a_key = lambda key: [convert(c) for c in re.split('([0-9]+)', key)] 7 | return sorted(lst, key=a_key) 8 | if __name__ == '__main__': 9 | a = ['r1', 'r1', 'r1', 'r4', 'r4', 'r7', 'r7', 'r7', 'r10', 'r10'] 10 | b = sorted(a) 11 | print("input - \n{}".format(a)) 12 | print("text sort - \n{}".format(b)) 13 | vals = natsort(a) 14 | print("natural sort - \n{}".format(vals)) -------------------------------------------------------------------------------- /field_calculator/natural_sort.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | """ 3 | :Script: natural_sort.py 4 | :Author: Dan.Patterson@carleton.ca 5 | :Modified: 2017-08-23 6 | :Purpose: Returns a mixed text-number list accounting for numeric values 7 | : ['a1', 'a20', 'a2', 'a10'] should yield ['a1', 'a2', 'a10', 'a20'] 8 | :Note: 9 | : C:\Git_Dan\JupyterNoteBooks\Short_Samples\Natural_sort.ipynb 10 | """ 11 | 12 | import re 13 | 14 | 15 | def natsort(text_lst): 16 | """natural sort returns text containing numbers sorted considering the 17 | : number in the sequence. 18 | :originals used lambda expressions 19 | : convert = lambda text: int(text) if text.isdigit() else text 20 | : a_key = lambda key: [convert(c) for c in re.split('([0-9]+)', key)] 21 | """ 22 | def convert(text): 23 | return int(text) if text.isdigit() else text 24 | 25 | def a_key(key): 26 | return [convert(c) for c in re.split('([0-9]+)', key)] 27 | 28 | return sorted(text_lst, key=a_key) 29 | 30 | 31 | # -------------------------------------------------------------------------- 32 | if __name__ == '__main__': 33 | """run with sample""" 34 | a = ['a1', 'a20', 'a2', 'a10'] 35 | vals = natsort(a) 36 | # print("natural sort - \n{}".format(vals)) 37 | -------------------------------------------------------------------------------- /field_calculator/pad_date.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | """ 3 | :Script: .py 4 | :Author: Dan.Patterson@carleton.ca 5 | :Modified: 2017-xx-xx 6 | :Purpose: tools for working with numpy arrays 7 | :Useage: 8 | : 9 | :References: 10 | : 11 | :---------------------------------------------------------------------: 12 | """ 13 | def pad_date(fld): 14 | """input a date field, strip off the time and format""" 15 | if fld is not None: 16 | lst = [int(i) for i in (str(fld).split(" ")[0]).split("-")] 17 | return "{}-{:02.0f}-{:02.0f}".format(*lst) 18 | else: 19 | return None 20 | 21 | # ---------------------------------------------------------------------- 22 | # __main__ .... code section 23 | if __name__ == "__main__": 24 | """ A simple test """ 25 | from datetime import datetime 26 | n = str(datetime.now()) 27 | print(pad_date(n)) -------------------------------------------------------------------------------- /field_calculator/pnt_along.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dan-Patterson/Tools_for_ArcGIS_Pro/6315d33469107b96c691a71b52ec695045a388c1/field_calculator/pnt_along.py -------------------------------------------------------------------------------- /field_calculator/rand_float.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | def rand_float(N=1, begin=0, end=10, deci=2): 3 | """ Generate N random floats within the range begin - end 4 | :Technically, N random integers are produced then a random 5 | :amount within 0-1 is added to the value 6 | """ 7 | float_vals = np.random.randint(begin, end, size=(N)) 8 | float_vals = np.around(float_vals + np.random.rand(N), deci) 9 | return float_vals 10 | __esri_field_calculator_splitter__ 11 | rand_float() -------------------------------------------------------------------------------- /field_calculator/rand_int.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | def rand_int(low=1, high=10): 3 | """ Generate a random integers within the range low - high 4 | """ 5 | num = np.random.randint(low, high) 6 | return num 7 | __esri_field_calculator_splitter__ 8 | rand_int() -------------------------------------------------------------------------------- /field_calculator/rand_norm.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | def rand_norm(N=10, avg_=10, st_dev=1, deci=2): 3 | """ Generate N random floats within the range begin - end 4 | :Technically, N random integers are produced then a random 5 | :amount within 0-1 is added to the value 6 | """ 7 | float_vals = np.random.normal(avg_, st_dev) #, size=(N)) 8 | float_vals = np.around(float_vals + np.random.rand(), deci) #(N), deci) 9 | return float_vals 10 | __esri_field_calculator_splitter__ 11 | rand_norm() -------------------------------------------------------------------------------- /field_calculator/seq_dup2.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | # (1) ... field calculator code block 3 | # Save as a *.cal file for loading directly into the code block. 4 | # Uncomment the last 2 lines to have the seq_dup line split out 5 | # 6 | fld = "" 7 | def seq_dup(val): 8 | """sequential duplicate checks""" 9 | global fld 10 | if val == fld: 11 | ret = 1 12 | else: 13 | ret = 0 14 | fld = val 15 | return ret 16 | #__esri_field_calculator_splitter__ # used by *.cal files 17 | #seq_dup(!Test!) # copy to expression section 18 | 19 | # (2) ... CalculateField format for use in scripts. 20 | # Uncomment in your IDE, insert the following lines into your script---- 21 | # 22 | #import arcpy 23 | #expr = ''' 24 | #fld = "" 25 | #def seq_dup(val): 26 | # """sequential duplicate checks""" 27 | # global fld 28 | # if val == fld: 29 | # ret = 1 30 | # else: 31 | # ret = 0 32 | # fld = val\ 33 | # return ret 34 | #''' 35 | #arcpy.management.CalculateField("f1", "IndFld", "seq_dup(!Test!), 36 | # "PYTHON_9.3", 37 | # expression=expr) 38 | # ---- End of CalculateField section 39 | 40 | if __name__ == "__main__": 41 | """Optionally... 42 | : - print the script source name. 43 | """ 44 | import inspect 45 | s = "".join([i for i in inspect.getsourcelines(seq_dup)[0]]) 46 | s = "{}\n{}".format('fld = ""', s) 47 | print(s) 48 | -------------------------------------------------------------------------------- /field_calculator/seq_group.py: -------------------------------------------------------------------------------- 1 | """Specify the number of records that you want to group in 'val' 2 | 'id' - a field containing sequential numbers or even random 3 | numbers. 4 | The number in 'id' is divided by 'val' and incremented if the 5 | modulus == 0 6 | """ 7 | cnt = 0 8 | def seq_group(id, val): 9 | global cnt 10 | if id % val == 0 : 11 | cnt += 1 12 | return cnt 13 | -------------------------------------------------------------------------------- /field_calculator/sequential_count.py: -------------------------------------------------------------------------------- 1 | """ 2 | Count the number of 'val's that exist in a field and number and format 3 | them, incrementing the number each time one is found 4 | """ 5 | old = "" 6 | cnt = 0 7 | def seq_count(val): 8 | global old 9 | global cnt 10 | if old == val: 11 | cnt += 1 12 | ret = "{} {:04.0f}".format(val, cnt) 13 | else: 14 | cnt = 0 15 | ret = "{} {:04.0f}".format(val, cnt) 16 | old = val 17 | return ret 18 | __esri_field_calculator_splitter__ 19 | seq_count(!Test!) -------------------------------------------------------------------------------- /field_calculator/sequential_dups.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | # (1) ... field calculator code block 3 | # Save as a *.cal file for loading directly into the code block. 4 | # Uncomment the last 2 lines to have the seq_dup line split out 5 | # 6 | fld = "" 7 | def seq_dup(val): 8 | """sequential duplicate checks""" 9 | global fld 10 | if val == fld: 11 | ret = 1 12 | else: 13 | ret = 0 14 | fld = val 15 | return ret 16 | #__esri_field_calculator_splitter__ # used by *.cal files 17 | #seq_dup(!Test!) # copy to expression section 18 | 19 | # (2) ... CalculateField format for use in scripts. 20 | # Uncomment in your IDE, insert the following lines into your script---- 21 | # 22 | #import arcpy 23 | #expr = ''' 24 | #fld = "" 25 | #def seq_dup(val): 26 | # """sequential duplicate checks""" 27 | # global fld 28 | # if val == fld: 29 | # ret = 1 30 | # else: 31 | # ret = 0 32 | # fld = val 33 | # return ret 34 | #''' 35 | #arcpy.management.CalculateField("f1", "IndFld", "seq_dup(!Test!), 36 | # "PYTHON_9.3", 37 | # expression=expr) 38 | # ---- End of CalculateField section 39 | 40 | if __name__ == "__main__": 41 | """Optionally... 42 | : - print the script source name. 43 | """ 44 | import inspect 45 | s = "".join([i for i in inspect.getsourcelines(seq_dup)[0]]) 46 | s = "{}\n{}".format('fld = ""', s) 47 | print(s) 48 | -------------------------------------------------------------------------------- /field_calculator/sequential_id.py: -------------------------------------------------------------------------------- 1 | cnt = 0 2 | def seq_count(val): 3 | global cnt 4 | if cnt >= val : 5 | cnt += 1 6 | return cnt 7 | 8 | __esri_field_calculator_splitter__ 9 | seq_count(0) -------------------------------------------------------------------------------- /field_calculator/shift_features.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | shift_features 4 | ============== 5 | 6 | Script : shift_features.py 7 | 8 | Author : Dan_Patterson@carleton.ca 9 | 10 | Modified : 2018-07-25 11 | """ 12 | 13 | import arcpy 14 | 15 | def shift_features(in_features, x_shift=None, y_shift=None): 16 | """ 17 | Shifts features by an x and/or y value. The shift values are in 18 | the units of the in_features coordinate system. 19 | 20 | Parameters: 21 | in_features: string 22 | An existing feature class or feature layer. If using a 23 | feature layer with a selection, only the selected features 24 | will be modified. 25 | 26 | x_shift: float 27 | The distance the x coordinates will be shifted. 28 | 29 | y_shift: float 30 | The distance the y coordinates will be shifted. 31 | """ 32 | 33 | with arcpy.da.UpdateCursor(in_features, ['SHAPE@XY']) as cursor: 34 | for row in cursor: 35 | cursor.updateRow([[row[0][0] + (x_shift or 0), 36 | row[0][1] + (y_shift or 0)]]) 37 | 38 | return -------------------------------------------------------------------------------- /field_calculator/string_defs.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | """ 3 | :Script: string_defs.py 4 | :Author: Dan.Patterson@carleton.ca 5 | :Modified: 2017-06-17 6 | :Purpose: tools for working strings 7 | :Useage: 8 | : These are mini-onliners or so 9 | 10 | :---------------------------------------------------------------------: 11 | """ 12 | # ---- imports, formats, constants ---- 13 | 14 | a = 'A string with numbers 10 20 in it' 15 | 16 | keep_text = "".join([i for i in a if i.isalpha() or i == " "]).strip() 17 | 18 | strip_spaces = " ".join([i.strip() for i in a.split(" ") if i != ""]) 19 | 20 | keep_numb = "".join([i for i in a if i.isdigit() or i == " "]).strip() 21 | 22 | num_csv = ", ".join([i for i in a.split() if i.isdigit() ]).strip() 23 | 24 | frmt = """ 25 | Input string......... {} 26 | 27 | Just text ........... {} 28 | Strip extra spaces .. {} 29 | Just numbers ........ {} 30 | Numbers to csv ...... {} 31 | 32 | """ 33 | args = [a, keep_text, strip_spaces, keep_numb, num_csv] 34 | print(frmt.format(*args)) 35 | -------------------------------------------------------------------------------- /field_calculator/strip_concatenate.py: -------------------------------------------------------------------------------- 1 | def strip_concatenate(in_flds, strip_list=[" ", ",", None]): 2 | """Provide the fields as a list ie [a, b, c] to strip spaces 3 | : and remove nulls 4 | : use: python parser 5 | : syntax: strip_stuff('!a!, !b!, !c!]) assumed field names 6 | """ 7 | fixed = [] 8 | fmt = [] 9 | for i in in_flds: 10 | if i not in strip_list: 11 | fixed.append(i) 12 | fmt.append("{}") 13 | frmt = " ".join([f for f in fmt]) 14 | frmt.strip() 15 | flds = [str(i).strip() for i in fixed] 16 | result = frmt.format(*fixed) 17 | return result 18 | __esri_field_calculator_splitter__ 19 | strip_concatenate() -------------------------------------------------------------------------------- /field_calculator/strip_time.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | """ 3 | :Script: strip_time.py 4 | :Author: Dan.Patterson@carleton.ca 5 | :Modified: 2017-09-14 6 | :Purpose: strip time of of a date-time 7 | :Useage: 8 | : 9 | :References: 10 | : 11 | :---------------------------------------------------------------------: 12 | """ 13 | def strip_time(fld): 14 | """input a date field, strip off the time and format""" 15 | if fld is not None: 16 | lst = [int(i) for i in (str(fld).split(" ")[0]).split("-")] 17 | return "{}-{:02.0f}-{:02.0f}".format(*lst) 18 | else: 19 | return None 20 | 21 | # ---------------------------------------------------------------------- 22 | # __main__ .... code section 23 | if __name__ == "__main__": 24 | """ A simple test """ 25 | from datetime import datetime 26 | n = str(datetime.now()) 27 | print(strip_time(n)) -------------------------------------------------------------------------------- /frequency_statistics/Frequency_Statistics.tbx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dan-Patterson/Tools_for_ArcGIS_Pro/6315d33469107b96c691a71b52ec695045a388c1/frequency_statistics/Frequency_Statistics.tbx -------------------------------------------------------------------------------- /frequency_statistics/README.md: -------------------------------------------------------------------------------- 1 | ## **Frequency_Statistics** ## 2 | The toolbox and script are contained in this folder 3 | -------------------------------------------------------------------------------- /geometry_tools/Densify.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dan-Patterson/Tools_for_ArcGIS_Pro/6315d33469107b96c691a71b52ec695045a388c1/geometry_tools/Densify.png -------------------------------------------------------------------------------- /geometry_tools/Phish_Nyet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dan-Patterson/Tools_for_ArcGIS_Pro/6315d33469107b96c691a71b52ec695045a388c1/geometry_tools/Phish_Nyet.png -------------------------------------------------------------------------------- /geometry_tools/angles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dan-Patterson/Tools_for_ArcGIS_Pro/6315d33469107b96c691a71b52ec695045a388c1/geometry_tools/angles.png -------------------------------------------------------------------------------- /geometry_tools/index: -------------------------------------------------------------------------------- 1 | Files included here 2 | -------------------------------------------------------------------------------- /sampling_grid/Readme.md: -------------------------------------------------------------------------------- 1 | **Sampling Grids** 2 | 3 | Sampling grids in the form of rectangles, triangles and hexagons (2 variants) 4 | 5 | Copy the `*.py` and the `*.tbx` files to a folder and load the toolbox into your project. Run the tool from there. 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /sampling_grid/SamplingGrid.tbx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dan-Patterson/Tools_for_ArcGIS_Pro/6315d33469107b96c691a71b52ec695045a388c1/sampling_grid/SamplingGrid.tbx -------------------------------------------------------------------------------- /sampling_grid/Sampling_grids.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dan-Patterson/Tools_for_ArcGIS_Pro/6315d33469107b96c691a71b52ec695045a388c1/sampling_grid/Sampling_grids.png -------------------------------------------------------------------------------- /sampling_grid/sampling_grid_dialog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dan-Patterson/Tools_for_ArcGIS_Pro/6315d33469107b96c691a71b52ec695045a388c1/sampling_grid/sampling_grid_dialog.png -------------------------------------------------------------------------------- /sampling_grid/sampling_grid_dialog2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dan-Patterson/Tools_for_ArcGIS_Pro/6315d33469107b96c691a71b52ec695045a388c1/sampling_grid/sampling_grid_dialog2.png -------------------------------------------------------------------------------- /statistics/Images/Column_Statistics.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dan-Patterson/Tools_for_ArcGIS_Pro/6315d33469107b96c691a71b52ec695045a388c1/statistics/Images/Column_Statistics.png -------------------------------------------------------------------------------- /statistics/Images/Column_Statistics02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dan-Patterson/Tools_for_ArcGIS_Pro/6315d33469107b96c691a71b52ec695045a388c1/statistics/Images/Column_Statistics02.png -------------------------------------------------------------------------------- /statistics/README.md: -------------------------------------------------------------------------------- 1 | ## Statistics for ArcGIS Pro ## 2 | 3 | Just download the zip file and unzip it where you want your toolbox to appear. The script will be contained in a folder. 4 | 5 | The toolbox was created to provide the capability to calculate descriptive statistics for 'double' fields in tables (geodatabase or dbase). It essentially enables you to select one, more than one or all fields. The sum, min, max, median, mean, std dev and var are determined. 6 | 7 | I have intentially left out integer fields from the available options since integers can be used to represent nominal categories or ranks and these statistics are not appropriate. 8 | 9 | The base code and the method for calculating values is for a column basis, but the code can quickly be exploited to provide information on a row basis. I will supplement the capabilities as need arises. 10 | -------------------------------------------------------------------------------- /statistics/Statistics.tbx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dan-Patterson/Tools_for_ArcGIS_Pro/6315d33469107b96c691a71b52ec695045a388c1/statistics/Statistics.tbx -------------------------------------------------------------------------------- /statistics/Statistics.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dan-Patterson/Tools_for_ArcGIS_Pro/6315d33469107b96c691a71b52ec695045a388c1/statistics/Statistics.zip -------------------------------------------------------------------------------- /triangulation_tools/Scripts/__pycache__/arcpytools_pnt.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dan-Patterson/Tools_for_ArcGIS_Pro/6315d33469107b96c691a71b52ec695045a388c1/triangulation_tools/Scripts/__pycache__/arcpytools_pnt.cpython-36.pyc -------------------------------------------------------------------------------- /triangulation_tools/Scripts/__pycache__/triangulate.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dan-Patterson/Tools_for_ArcGIS_Pro/6315d33469107b96c691a71b52ec695045a388c1/triangulation_tools/Scripts/__pycache__/triangulate.cpython-36.pyc -------------------------------------------------------------------------------- /triangulation_tools/triangulation_tools.tbx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dan-Patterson/Tools_for_ArcGIS_Pro/6315d33469107b96c691a71b52ec695045a388c1/triangulation_tools/triangulation_tools.tbx -------------------------------------------------------------------------------- /triangulation_tools/voronoi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dan-Patterson/Tools_for_ArcGIS_Pro/6315d33469107b96c691a71b52ec695045a388c1/triangulation_tools/voronoi.png --------------------------------------------------------------------------------