├── .gitignore
├── CONTRIBUTING.md
├── License.txt
├── README.md
├── functions
├── BasicChuckClose.py
├── BasicCubism.py
├── BlockStatistics.py
├── BlockStatistics.rft.xml
├── CompositeBands-4Bands-Ordered.rft.xml
├── CompoundTopographicIndex.py
├── CompoundTopographicIndex_64bitScipy.py
├── Cythonize.py
├── FillRaster.py
├── FillRaster.rft.xml
├── FindMax.py
├── FindMaxPixel_Mosaic.rft.xml
├── FindSecondMax.py
├── FindSecondMaxPixel_Mosaic.rft.xml
├── FindThirdMax.py
├── FindThirdMaxPixel_Mosaic.rft.xml
├── FishHabitatSuitability.py
├── FishHabitatSuitability.rft.xml
├── FuzzyMembership.py
├── GradientBoostedClassifier.py
├── HexagonPixels.py
├── KNearestNeighborsClassifier.py
├── Landsat ETM Pixel Percentile.rft.xml
├── Landsat ETM Scene Synthesis.rft.xml
├── Landsat Image Synthesis.rft.xml
├── Landsat OLI Pixel Percentile.rft.xml
├── Landsat OLI Scene Synthesis.rft.xml
├── Landsat TM Pixel Percentile.rft.xml
├── Landsat TM Scene Synthesis.rft.xml
├── LandsatC2QA.py
├── LandsatImageSynthesis.py
├── LandsatImageSynthesis.rft.xml
├── LandsatMedianImage.py
├── LandsatMedianPixelComposite.py
├── LandsatPixelPercentile.py
├── Landsat_Image_Synthesis.py
├── MaskRaster.py
├── MaskRaster.rft.xml
├── NearestNeighborClassifier_SKLearn.py
├── NearestNeighborsClassifier.py
├── PercentAboveThreshold.py
├── PercentAboveThreshold.rft.xml
├── RandomForestClassifier.py
├── RankFilter.py
├── RankFilter.rft.xml
├── Reference.py
├── RemoveNoData.rft.xml
├── ReplaceNulls.py
├── ReplaceNulls.rft.xml
├── SeasonalARIMA.py
├── SeasonalARIMA.rft.xml
├── SelectByPixelSize.py
├── SelectByPixelSize.rft.xml
├── StepwiseLocalRadiometricAdjustment.py
├── StepwiseLocalRadiometricAdjustment.rft.xml
├── TerrainRuggednessIndex-Riley-Colormap.rft.xml
├── TopographicCCorrection.py
├── TopographicCCorrection.rft.xml
├── VF.rft.xml
├── VineyardAnalysis.py
├── VineyardAnalysis.rft.xml
├── deprecated
│ ├── Aggregate.py
│ ├── Aggregate.rft.xml
│ ├── Arithmetic.py
│ ├── AspectSlope.py
│ ├── CompositeBands.rft.xml
│ ├── ConvertPerSecondToPerMonth.py
│ ├── ConvertPerSecondToPerMonth.rft.xml
│ ├── DeviationFromMean.rft.xml
│ ├── DifferencedNormalizedBurnRatio.py
│ ├── FocalStatistics.rft.xml
│ ├── HeatIndex.py
│ ├── HeatIndex.rft.xml
│ ├── Hillshade-ScaleAdjusted-Py.rft.xml
│ ├── Hillshade.py
│ ├── KeyMetadata.py
│ ├── LinearSpectralUnmixing.py
│ ├── LinearSpectralUnmixing.rft.xml
│ ├── MergeRasters.rft.xml
│ ├── MultidirectionalHillshade.rft.xml
│ ├── NDVI-Colormap.rft.xml
│ ├── NDVI-Grayscale.rft.xml
│ ├── NDVI.py
│ ├── NDVI.rft.xml
│ ├── Normalized-Difference Snow Index (NDSI) for Landsat OLI.rft.xml
│ ├── Normalized-Difference Water Index (NDWI) for Landsat OLI.rft.xml
│ ├── Random.py
│ ├── Random.rft.xml
│ ├── RasterizeAttributes.py
│ ├── Subtract.rft.xml
│ ├── Windchill.py
│ ├── Windchill.rft.xml
│ ├── ZonalRemap.py
│ └── ZonalRemap.rft.xml
├── functions.pyproj
└── utils.py
├── raster-functions.sln
├── scripts
├── ExtractRasterInfo.py
├── distribution.txt
├── get-pip.py
└── requirements.txt
└── setup.py
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 |
5 | # C extensions
6 | *.so
7 |
8 | # Distribution / packaging
9 | .Python
10 | env/
11 | bin/
12 | build/
13 | develop-eggs/
14 | dist/
15 | eggs/
16 | lib/
17 | lib64/
18 | parts/
19 | sdist/
20 | var/
21 | *.egg-info/
22 | .installed.cfg
23 | *.egg
24 |
25 | # Installer logs
26 | pip-log.txt
27 | pip-delete-this-directory.txt
28 |
29 | # Unit test / coverage reports
30 | htmlcov/
31 | .tox/
32 | .coverage
33 | .cache
34 | nosetests.xml
35 | coverage.xml
36 |
37 | # Translations
38 | *.mo
39 |
40 | # Mr Developer
41 | .mr.developer.cfg
42 | .project
43 | .pydevproject
44 |
45 | # Rope
46 | .ropeproject
47 |
48 | # Django stuff:
49 | *.log
50 | *.pot
51 |
52 | # Sphinx documentation
53 | docs/_build/
54 |
55 | # Visual Studio files
56 | *.suo
57 | .idea/workspace.xml
58 | .idea/vcs.xml
59 | .idea/modules.xml
60 | .idea/misc.xml
61 | .idea/gbrunner-raster-functions.iml
62 | .idea
63 | pickles/
64 | functions/Landsat8PixelPercentile.py
65 | .vs/ProjectSettings.json
66 | .vs/slnx.sqlite
67 | .vs/*
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | Esri welcomes contributions from anyone and everyone. Please see our [guidelines for contributing](https://github.com/esri/contributing).
--------------------------------------------------------------------------------
/functions/BasicChuckClose.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | import math
3 |
4 | class BasicChuckClose():
5 |
6 | def __init__(self):
7 | self.name = "Basic Chuck Close"
8 | self.description = ("This python raster function is the first of which I "
9 | "plan to create that will transform imagery into artwork reminiscent of "
10 | "Chuck Close.")
11 |
12 | def getParameterInfo(self):
13 | return [
14 | {
15 | 'name': 'dem',
16 | 'dataType': 'raster',
17 | 'value': None,
18 | 'required': True,
19 | 'displayName': "DEM Raster",
20 | 'description': "The digital elevation model (DEM)."
21 | },
22 | {
23 | 'name': 'inv',
24 | 'dataType': 'boolean',
25 | 'value': True,
26 | 'required': True,
27 | 'displayName': "Invert?",
28 | 'description': "Inverts the image so that higher elevations are darker."
29 | },
30 | {
31 | 'name': 'show_pix',
32 | 'dataType': 'boolean',
33 | 'value': False,
34 | 'required': True,
35 | 'displayName': "Colorize Pixels?",
36 | 'description': "Gives pixels colors corresponding to their elevation."
37 | }
38 | ]
39 |
40 | def getConfiguration(self, **scalars):
41 | return {
42 | 'compositeRasters': False,
43 | 'inheritProperties': 1 | 2 | 4 | 8, # inherit all from the raster
44 | 'invalidateProperties': 2 | 4 | 8, # reset stats, histogram, key properties
45 | 'inputMask': False
46 | }
47 |
48 | def updateRasterInfo(self, **kwargs):
49 | # repeat stats for all output raster bands
50 | kwargs['output_info']['bandCount'] = 1
51 | kwargs['output_info']['histogram'] = () # reset histogram
52 | kwargs['output_info']['pixelType'] = 'u1'
53 | kwargs['output_info']['noData'] = np.array([0], 'u1')
54 | self.invert = kwargs.get('inv')
55 | self.show_pix = kwargs.get('show_pix')
56 | if not self.show_pix:
57 | kwargs['output_info']['statistics'] = ({'minimum': 0, 'maximum': 1.0}, )
58 | return kwargs
59 |
60 | def updatePixels(self, tlc, shape, props, **pixelBlocks):
61 | #file = open(r'C:\PROJECTS\gbrunner-raster-functions\test.txt','w')
62 | #file.write(str(z))
63 | # get the input DEM raster pixel block
64 | inBlock_dem = pixelBlocks['dem_pixels']
65 | z = inBlock_dem.shape
66 | #file.write(str(z)+'\n')
67 | x, y = z[1],z[2]
68 | chuck_close = np.zeros(z)
69 | square_size = 13
70 | pixel_buffer = 7
71 | maximum = np.max(inBlock_dem)
72 | minimum = np.min(inBlock_dem)
73 | spread = maximum-minimum
74 | break_size = spread/((pixel_buffer-1))
75 | class_breaks = {}
76 | for i in range(0,int(pixel_buffer)):
77 | class_breaks[i] = minimum+i*break_size
78 | num_squares_x = math.floor(x/square_size)
79 | num_squares_y = math.floor(y/square_size)
80 | #file.write(str(num_squares_x)+'\n')
81 | #file.write(str(num_squares_y)+'\n')
82 |
83 | for num_x in range(1,int(num_squares_x)):
84 | for num_y in range(1,int(num_squares_y)):
85 |
86 | pix = np.mean(inBlock_dem[0,num_x*square_size:(num_x+1)*square_size, num_y*square_size:(num_y+1)*square_size])
87 | pixel_buffer = get_size(pix, class_breaks)
88 |
89 | ## chuck_close[num_x*square_size-pixel_buffer:num_x*square_size+pixel_buffer, num_y*square_size-pixel_buffer:num_y*square_size+pixel_buffer] = 1 #np.mean(dem[num_x*square_size+pixel_buffer:(num_x+1)*square_size-pixel_buffer, num_y*square_size+pixel_buffer:(num_y+1)*square_size-pixel_buffer])#dem[num_x*square_size, num_y*square_size]#-1*dem[num_x*square_size, num_y*square_size]+maximum
90 | if self.invert:
91 | if self.show_pix:
92 | chuck_close[0,num_x*square_size+pixel_buffer:(num_x+1)*square_size-pixel_buffer, num_y*square_size+pixel_buffer:(num_y+1)*square_size-pixel_buffer]= pix #1 #np.mean(dem[num_x*square_size+pixel_buffer:(num_x+1)*square_size-pixel_buffer, num_y*square_size+pixel_buffer:(num_y+1)*square_size-pixel_buffer])#dem[num_x*square_size, num_y*square_size]#-1*dem[num_x*square_size, num_y*square_size]+maximum
93 | else:
94 | chuck_close[0,num_x*square_size+pixel_buffer:(num_x+1)*square_size-pixel_buffer, num_y*square_size+pixel_buffer:(num_y+1)*square_size-pixel_buffer]= 1
95 |
96 | else:
97 | if self.show_pix:
98 | chuck_close[0,num_x*square_size-pixel_buffer:num_x*square_size+pixel_buffer, num_y*square_size-pixel_buffer:num_y*square_size+pixel_buffer] = pix #1 #np.mean(dem[num_x*square_size+pixel_buffer:(num_x+1)*square_size-pixel_buffer, num_y*square_size+pixel_buffer:(num_y+1)*square_size-pixel_buffer])#dem[num_x*square_size, num_y*square_size]#-1*dem[num_x*square_size, num_y*square_size]+maximum
99 | else:
100 | chuck_close[0,num_x*square_size-pixel_buffer:num_x*square_size+pixel_buffer, num_y*square_size-pixel_buffer:num_y*square_size+pixel_buffer] = 1
101 |
102 | # format output cti pixels
103 | outBlocks = chuck_close.astype(props['pixelType'], copy=False)
104 | #file.write(outBlocks)
105 | #file.close()
106 | pixelBlocks['output_pixels'] = outBlocks
107 | return pixelBlocks
108 |
109 | def updateKeyMetadata(self, names, bandIndex, **keyMetadata):
110 | if bandIndex == -1: # dataset level
111 | keyMetadata['datatype'] = 'Scientific'
112 | else: # output "band"
113 | keyMetadata['wavelengthmin'] = None
114 | keyMetadata['wavelengthmax'] = None
115 | keyMetadata['bandname'] = 'GIS is Art'
116 | return keyMetadata
117 |
118 |
119 | def get_size(pixel_val, d):
120 |
121 | diff = float('inf')
122 | for key,value in d.items():
123 | if diff > abs(pixel_val-value):
124 | diff = abs(pixel_val-value)
125 | x = key
126 |
127 | if diff==float('inf'):
128 | x = 1
129 |
130 | return x
131 |
--------------------------------------------------------------------------------
/functions/BasicCubism.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | import math
3 |
4 | class BasicCubism():
5 |
6 | def __init__(self):
7 | self.name = "Basic Cubism"
8 | self.description = ("This python raster function sets the foundation for "
9 | "making cubism maps. The takes a block of pixels, subdivides them into "
10 | "larger chunks, and colorizes them by their elevation.")
11 |
12 | def getParameterInfo(self):
13 | return [
14 | {
15 | 'name': 'dem',
16 | 'dataType': 'raster',
17 | 'value': None,
18 | 'required': True,
19 | 'displayName': "DEM Raster",
20 | 'description': "The digital elevation model (DEM)."
21 | }
22 | ]
23 |
24 | def getConfiguration(self, **scalars):
25 | return {
26 | 'compositeRasters': False,
27 | 'inheritProperties': 1 | 2 | 4 | 8, # inherit all from the raster
28 | 'invalidateProperties': 2 | 4 | 8, # reset stats, histogram, key properties
29 | 'inputMask': False
30 | }
31 |
32 | def updateRasterInfo(self, **kwargs):
33 | # repeat stats for all output raster bands
34 | kwargs['output_info']['bandCount'] = 1
35 | kwargs['output_info']['histogram'] = () # reset histogram
36 | kwargs['output_info']['pixelType'] = 'f4'
37 | kwargs['output_info']['noData'] = np.array([0], 'f4')
38 | return kwargs
39 |
40 | def updatePixels(self, tlc, shape, props, **pixelBlocks):
41 | # get the input DEM raster pixel block
42 | inBlock_dem = pixelBlocks['dem_pixels']
43 |
44 | z = inBlock_dem.shape
45 | x, y = z[1],z[2]
46 | cubism = np.zeros(z)
47 |
48 | #init_pixel_buffer = 10 change this
49 | pixel_buffer = 1
50 | square_size = 5
51 | maximum = np.max(inBlock_dem)
52 | minimum = np.min(inBlock_dem)
53 | spread = maximum-minimum
54 | break_size = spread/((square_size-1)-1)
55 | class_breaks = {}
56 | for i in range(1,int(square_size)-1):
57 | class_breaks[i] = minimum+i*break_size
58 | num_squares_x = math.floor(x/square_size)
59 | num_squares_y = math.floor(y/square_size)
60 |
61 | for num_x in range(0,int(num_squares_x)):
62 | for num_y in range(0,int(num_squares_y)):
63 | #chuck_close[num_x*square_size:(num_x+1)*square_size, num_y*square_size:(num_y+1)*square_size] = mean #dem[num_x*square_size, num_y*square_size]
64 | cubism[0,num_x*square_size+pixel_buffer:(num_x+1)*square_size-pixel_buffer, num_y*square_size+pixel_buffer:(num_y+1)*square_size-pixel_buffer] = np.mean(inBlock_dem[0,num_x*square_size+pixel_buffer:(num_x+1)*square_size-pixel_buffer, num_y*square_size+pixel_buffer:(num_y+1)*square_size-pixel_buffer])#dem[num_x*square_size, num_y*square_size]#-1*dem[num_x*square_size, num_y*square_size]+maximum
65 |
66 | # format output pixels
67 | outBlocks = cubism.astype(props['pixelType'], copy=False)
68 | pixelBlocks['output_pixels'] = outBlocks
69 | return pixelBlocks
70 |
71 | def updateKeyMetadata(self, names, bandIndex, **keyMetadata):
72 | if bandIndex == -1: # dataset level
73 | keyMetadata['datatype'] = 'Scientific'
74 | else: # output "band"
75 | keyMetadata['wavelengthmin'] = None
76 | keyMetadata['wavelengthmax'] = None
77 | keyMetadata['bandname'] = 'CTI'
78 | return keyMetadata
79 |
--------------------------------------------------------------------------------
/functions/BlockStatistics.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | from skimage.transform import resize
3 | from skimage.util import view_as_blocks
4 |
5 |
6 | class BlockStatistics():
7 |
8 | def __init__(self):
9 | self.name = "Block Statistics Function"
10 | self.description = ("Generates a downsampled output raster by computing a statistical "
11 | "measure over non-overlapping square blocks of pixels in the input raster.")
12 | self.func = np.mean
13 | self.padding = 0
14 |
15 | def getParameterInfo(self):
16 | return [
17 | {
18 | 'name': 'raster',
19 | 'dataType': 'raster',
20 | 'value': None,
21 | 'required': True,
22 | 'displayName': "Input Raster",
23 | 'description': "The primary input raster over which block statistics is computed."
24 | },
25 | {
26 | 'name': 'size',
27 | 'dataType': 'numeric',
28 | 'value': 1,
29 | 'required': False,
30 | 'displayName': "Block Size",
31 | 'description': ("The number of pixels along each side of the square "
32 | "non-overlapping block.")
33 | },
34 | {
35 | 'name': 'measure',
36 | 'dataType': 'string',
37 | 'value': 'Mean',
38 | 'required': False,
39 | 'displayName': "Measure",
40 | 'domain': ('Minimum', 'Maximum', 'Mean', 'Median', 'Sum', 'Nearest'),
41 | 'description': ("The statistical measure computed over each "
42 | "block of pixels in the input raster.")
43 | },
44 | {
45 | 'name': 'factor',
46 | 'dataType': 'numeric',
47 | 'value': 1,
48 | 'required': False,
49 | 'displayName': "Downsampling Factor",
50 | 'description': ("The integer factor by which the output raster is "
51 | "downsampled relative to the input raster.")
52 | },
53 | ]
54 |
55 | def getConfiguration(self, **scalars):
56 | s = scalars.get('size', None)
57 | s = 3 if s is None else s
58 | self.padding = int(s / 2)
59 | return {
60 | 'samplingFactor': scalars.get('size', 1.0),
61 | 'inheritProperties': 4 | 8, # inherit everything but the pixel type (1) and NoData (2)
62 | 'invalidateProperties': 2 | 4 | 8, # invalidate histogram, statistics, and key metadata
63 | 'inputMask': True,
64 | 'resampling': False,
65 | 'padding': self.padding,
66 | }
67 |
68 | def updateRasterInfo(self, **kwargs):
69 | f = kwargs.get('factor', 1.0)
70 | kwargs['output_info']['cellSize'] = tuple(np.multiply(kwargs['raster_info']['cellSize'], f))
71 | kwargs['output_info']['pixelType'] = 'f4' # output pixels values are floating-point
72 | kwargs['output_info']['statistics'] = ()
73 | kwargs['output_info']['histogram'] = ()
74 |
75 | m = kwargs.get('measure')
76 | m = m.lower() if m is not None and len(m) else 'mean'
77 |
78 | if m == 'minimum':
79 | self.func = np.min
80 | elif m == 'maximum':
81 | self.func = np.max
82 | elif m == 'mean':
83 | self.func = np.mean
84 | elif m == 'median':
85 | self.func = np.median
86 | elif m == 'sum':
87 | self.func = np.sum
88 | elif m == 'nearest':
89 | self.func = None
90 |
91 | return kwargs
92 |
93 | def updatePixels(self, tlc, shape, props, **pixelBlocks):
94 | p = pixelBlocks['raster_pixels']
95 | m = pixelBlocks['raster_mask']
96 |
97 | if self.func is None:
98 | b = resize(p, shape, order=0, preserve_range=True)
99 | else:
100 | blockSizes = tuple(np.divide(p.shape, shape))
101 | b = np.ma.masked_array(view_as_blocks(p, blockSizes),
102 | view_as_blocks(~m.astype('b1'), blockSizes))
103 | for i in range(len(b.shape) // 2):
104 | b = self.func(b, axis=-1)
105 | b = b.data
106 |
107 | d = self.padding
108 | pixelBlocks['output_pixels'] = b.astype(props['pixelType'], copy=False)
109 | pixelBlocks['output_mask'] = resize(m, shape, order=0, preserve_range=True).astype('u1', copy=False)
110 | return pixelBlocks
111 |
112 | def updateKeyMetadata(self, names, bandIndex, **keyMetadata):
113 | if bandIndex == -1:
114 | keyMetadata['datatype'] = 'Processed'
115 | return keyMetadata
116 |
--------------------------------------------------------------------------------
/functions/BlockStatistics.rft.xml:
--------------------------------------------------------------------------------
1 |
2 | BlockStatistics
3 | Compute statistical measure over non-overlapping blocks over input raster.
4 |
5 | Block Statistics Function
6 | Generates a downsampled output raster by computing a statistical measure over non-overlapping square blocks of pixels in the input raster.
7 | UNKNOWN
8 |
9 |
10 |
11 | PythonModule
12 | ClassName
13 | raster
14 | size
15 | measure
16 | factor
17 |
18 |
19 | BlockStatistics.py
20 | BlockStatistics
21 |
22 | Raster
23 |
24 |
25 | true
26 |
27 |
28 | size
29 |
30 | 5
31 | false
32 |
33 |
34 | measure
35 |
36 | Mean
37 | false
38 |
39 |
40 | factor
41 |
42 | 10
43 | false
44 |
45 |
46 |
47 |
48 | 0
49 |
50 |
51 |
52 |
53 |
54 |
--------------------------------------------------------------------------------
/functions/CompositeBands-4Bands-Ordered.rft.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | CompositeBands-4Bands-Ordered
4 | A raster function template.
5 |
6 | Composite Band Function
7 | Combines rasters to form a multiband raster.
8 | UNKNOWN
9 |
10 |
11 | Rasters_2014515_171526_412
12 |
13 |
14 |
15 | Raster1
16 |
17 |
18 |
19 | RasterAlias1
20 |
21 | true
22 |
23 |
24 | Raster2
25 |
26 |
27 |
28 | RasterAlias2
29 |
30 | true
31 |
32 |
33 | Raster3
34 |
35 |
36 |
37 | RasterAlias3
38 |
39 | true
40 |
41 |
42 | Raster4
43 |
44 |
45 |
46 | RasterAlias4
47 |
48 | true
49 |
50 |
51 | false
52 |
53 |
54 | 2
55 |
56 |
57 | GroupName
58 | Tag
59 |
--------------------------------------------------------------------------------
/functions/CompoundTopographicIndex.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | from numpy import pi
3 | from math import sqrt
4 |
5 | class CompoundTopographicIndex():
6 |
7 | def __init__(self):
8 | self.name = "Compound Topographic Index"
9 | self.description = ("Computes the compound topographic index (CTI), also "
10 | "known as the topographic wetness index (TWI). "
11 | "This is calculated from an input slope raster and flow "
12 | "accumulation surface and is meant to be used in "
13 | "a raster function chain.")
14 |
15 | def getParameterInfo(self):
16 | return [
17 | {
18 | 'name': 'slope',
19 | 'dataType': 'raster',
20 | 'value': None,
21 | 'required': True,
22 | 'displayName': "Slope Raster",
23 | 'description': "A slope raster (in degrees) derived from a digital elevation model (DEM)."
24 | },
25 | {
26 | 'name': 'flow',
27 | 'dataType': 'raster',
28 | 'value': None,
29 | 'required': True,
30 | 'displayName': "Flow Accumulation Raster",
31 | 'description': "A raster representing flow accumulation."
32 | }
33 | ]
34 |
35 | def getConfiguration(self, **scalars):
36 | return {
37 | 'compositeRasters': False,
38 | 'inheritProperties': 1 | 2 | 4 | 8, # inherit all from the raster
39 | 'invalidateProperties': 2 | 4 | 8, # reset stats, histogram, key properties
40 | 'inputMask': False
41 | }
42 |
43 | def updateRasterInfo(self, **kwargs):
44 | # repeat stats for all output raster bands
45 | kwargs['output_info']['bandCount'] = 1
46 | kwargs['output_info']['statistics'] = ({'minimum': 0, 'maximum': 25.0}, )
47 | kwargs['output_info']['histogram'] = () # reset histogram
48 | kwargs['output_info']['pixelType'] = 'f4'
49 | self.dem_cellsize = kwargs['slope_info']['cellSize']
50 | return kwargs
51 |
52 | def updatePixels(self, tlc, shape, props, **pixelBlocks):
53 | # get the input DEM raster pixel block
54 | inBlock_slope = pixelBlocks['slope_pixels']
55 | inBlock_flow = pixelBlocks['flow_pixels']
56 | cellSize = self.dem_cellsize
57 |
58 | DX = cellSize[0]
59 | DY = cellSize[1]
60 | slope = calc_slope(inBlock_slope)
61 | cti = calc_cti(slope, inBlock_flow, cellSize[0])
62 |
63 | # format output cti pixels
64 | outBlocks = cti.astype(props['pixelType'], copy=False)
65 | pixelBlocks['output_pixels'] = outBlocks
66 | return pixelBlocks
67 |
68 | def updateKeyMetadata(self, names, bandIndex, **keyMetadata):
69 | if bandIndex == -1: # dataset level
70 | keyMetadata['datatype'] = 'Scientific'
71 | else: # output "band"
72 | keyMetadata['wavelengthmin'] = None
73 | keyMetadata['wavelengthmax'] = None
74 | keyMetadata['bandname'] = 'CTI'
75 | return keyMetadata
76 |
77 | # supporting business logic functions
78 | def calc_slope(slope_deg):
79 | slope = slope_deg * pi/180 #np.arctan(slope_deg)
80 | return slope
81 |
82 | def calc_cti(slope, flow_acc, cellsize):
83 | tan_slope = np.tan(slope)
84 | tan_slope[tan_slope==0]=0.0001
85 | cti = np.log(((flow_acc+1)*cellsize)/tan_slope)
86 | return cti
87 |
--------------------------------------------------------------------------------
/functions/CompoundTopographicIndex_64bitScipy.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | from math import sqrt
3 | from scipy import sparse as sp
4 | from scipy.sparse import linalg as splg
5 |
6 | class CompoundTopographicIndex_64bitScipy():
7 |
8 | def __init__(self):
9 | self.name = "Compound Topographic Index"
10 | self.description = ("Computes the compound topographic index (CTI), also "
11 | "known as the topographic wetness index (TWI).")
12 |
13 | def getParameterInfo(self):
14 | return [
15 | {
16 | 'name': 'dem',
17 | 'dataType': 'raster',
18 | 'value': None,
19 | 'required': True,
20 | 'displayName': "DEM Raster",
21 | 'description': "The digital elevation model (DEM)."
22 | }
23 | ]
24 |
25 | def getConfiguration(self, **scalars):
26 | return {
27 | 'compositeRasters': False,
28 | 'inheritProperties': 1 | 2 | 4 | 8, # inherit all from the raster
29 | 'invalidateProperties': 2 | 4 | 8, # reset stats, histogram, key properties
30 | 'inputMask': False
31 | }
32 |
33 | def updateRasterInfo(self, **kwargs):
34 | # repeat stats for all output raster bands
35 | kwargs['output_info']['bandCount'] = 1
36 | kwargs['output_info']['statistics'] = ({'minimum': 0, 'maximum': 25.0}, )
37 | kwargs['output_info']['histogram'] = () # reset histogram
38 | kwargs['output_info']['pixelType'] = 'f4'
39 | self.dem_cellsize = kwargs['dem_info']['cellSize']
40 | return kwargs
41 |
42 | def updatePixels(self, tlc, shape, props, **pixelBlocks):
43 | # get the input DEM raster pixel block
44 | inBlock_dem = pixelBlocks['dem_pixels']
45 | cellSize = self.dem_cellsize
46 |
47 | slope = calc_slope(inBlock_dem[0,:,:], cellSize[0])
48 | DX = cellSize[0]
49 | DY = cellSize[1]
50 | flow_direction = calc_flow_direction_d8(DX, DY, inBlock_dem[0,:,:])
51 | flow_accumulation = calc_flow_accumulation(flow_direction, inBlock_dem.shape)
52 | cti = calc_cti(slope, flow_accumulation, cellSize[0])
53 |
54 | # format output cti pixels
55 | outBlocks = cti.astype(props['pixelType'], copy=False)
56 | pixelBlocks['output_pixels'] = outBlocks
57 | return pixelBlocks
58 |
59 | def updateKeyMetadata(self, names, bandIndex, **keyMetadata):
60 | if bandIndex == -1: # dataset level
61 | keyMetadata['datatype'] = 'Scientific'
62 | else: # output "band"
63 | keyMetadata['wavelengthmin'] = None
64 | keyMetadata['wavelengthmax'] = None
65 | keyMetadata['bandname'] = 'CTI'
66 | return keyMetadata
67 |
68 |
69 | # supporting business logic functions
70 | def calc_slope(dem, cellsize):
71 | #Modified from calculation found here:
72 | #http://geoexamples.blogspot.com/2014/03/shaded-relief-images-using-gdal-python.html
73 |
74 | x, y = np.gradient(dem, cellsize, cellsize)
75 | #slope = np.pi/2.0 - np.arctan(np.sqrt(x*x + y*y))
76 | slope = np.arctan(np.sqrt(x*x + y*y))
77 | return slope
78 |
79 |
80 | def calc_flow_direction_d8(DX, DY, dem):
81 | #Backgroud found at http://adh.usace.army.mil/new_webpage/main/main_page.htm
82 | #Algorithm modified from http://adh.usace.army.mil/svn/adh/mfarthin/src/samsi/2013/topo/
83 |
84 | nr = dem.shape[0]
85 | nc = dem.shape[1]
86 | HYP = sqrt(DX*DX + DY*DY)
87 |
88 | ghost = np.zeros((nr+2, nc+2), 'd')
89 | ghost[1:-1, 1:-1] = dem[:, :]
90 | ghost[0, 1:-1] = dem[0, :]
91 | ghost[-1, 1:-1] = dem[-1, :]
92 | ghost[1:-1, 0] = dem[:, 0]
93 | ghost[1:-1, -1] = dem[:, -1]
94 |
95 | ghost[0, 0] = ghost[1, 1]
96 | ghost[-1, -1] = ghost[-2, -2]
97 |
98 | ghost[0, -1] = ghost[1, -2]
99 | ghost[-1, 0] = ghost[-2, 1]
100 |
101 | neig_incr_col_major = np.array([nr,
102 | nr-1, -1, -nr-1,
103 | -nr,
104 | -nr+1, 1, nr+1])
105 |
106 | neig_incr = neig_incr_col_major
107 | slopes = np.zeros((8,), 'd')
108 | all_indices = np.arange(nr*nc, dtype='i')
109 | max_indices = np.zeros(nr*nc, 'i')
110 | slope_vals = np.zeros(nr*nc, 'd')
111 | slope_count = np.zeros(nr*nc, 'i')
112 | for i in range(1, nr+1):
113 | for j in range(1, nc+1):
114 | #
115 | slopes[0] = (ghost[i, j]-ghost[i, j+1])/DX
116 | slopes[4] = (ghost[i, j]-ghost[i, j-1])/DX
117 | #
118 | slopes[1] = (ghost[i, j]-ghost[i-1, j+1])/HYP
119 | slopes[2] = (ghost[i, j]-ghost[i-1, j])/DY
120 | slopes[3] = (ghost[i, j]-ghost[i-1, j-1])/HYP
121 | #
122 | slopes[5] = (ghost[i, j]-ghost[i+1, j-1])/HYP
123 | slopes[6] = (ghost[i, j]-ghost[i+1, j])/DY
124 | slopes[7] = (ghost[i, j]-ghost[i+1, j+1])/HYP
125 |
126 | glob_ind = (j-1)*nr + i-1 # local cell col major
127 | loc_max = slopes.argmax()
128 | if slopes[loc_max] > 0:
129 | glob_max = min(max(glob_ind + neig_incr[loc_max], 0), nc*nr-1)
130 | max_indices[glob_ind] = glob_max
131 | slope_vals[glob_ind] = slopes[loc_max]
132 | slope_count[glob_ind] = 1
133 |
134 | M = sp.csr_matrix((slope_count, (all_indices, max_indices)), shape=(nr*nc, nr*nc))
135 | return M
136 |
137 |
138 | def calc_flow_accumulation(M, dsh):
139 | #Backgroud found at http://adh.usace.army.mil/new_webpage/main/main_page.htm
140 | #Algorithm modified from http://adh.usace.army.mil/svn/adh/mfarthin/src/samsi/2013/topo/
141 |
142 | nc = M.shape[1]
143 | I = sp.eye(M.shape[0], M.shape[1])
144 | B = I - M.transpose()
145 | b = np.ones(nc, 'd')
146 | #a = sp.linalg.spsolve.spsolve(B, b)
147 | a = splg.spsolve(B, b)
148 | a = a.reshape(dsh, order='F')
149 | return a
150 |
151 |
152 | def calc_cti(slope, flow_acc, cellsize):
153 | #Based on background infotmation found at
154 | #http://gis4geomorphology.com/topographic-index-model/
155 | #and
156 | #http://gis.stackexchange.com/questions/43276/can-compound-topographic-index-cti-topographic-wetness-index-twi-produce-n
157 | #and
158 | #https://wikispaces.psu.edu/display/AnthSpace/Compound+Topographic+Index
159 |
160 | tan_slope = np.tan(slope)
161 | tan_slope[tan_slope==0]=0.0001
162 | cti = np.log(((flow_acc+1)*cellsize)/tan_slope)
163 | return cti
164 |
--------------------------------------------------------------------------------
/functions/Cythonize.py:
--------------------------------------------------------------------------------
1 | """
2 | Cythonize.py build_ext --inplace
3 | Cythonize.py clean
4 |
5 | """
6 |
7 | from distutils.core import setup
8 | from Cython.Build import cythonize
9 |
10 | setup(ext_modules = cythonize("*.py"))
11 |
--------------------------------------------------------------------------------
/functions/FillRaster.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 |
3 |
4 | class FillRaster():
5 |
6 | def __init__(self):
7 | self.name = "Fill Raster Function"
8 | self.description = ("")
9 | self.fillValue = 0.
10 |
11 | def getParameterInfo(self):
12 | return [
13 | {
14 | 'name': 'raster',
15 | 'dataType': 'raster',
16 | 'value': None,
17 | 'required': True,
18 | 'displayName': "Input Raster",
19 | 'description': ""
20 | },
21 | {
22 | 'name': 'value',
23 | 'dataType': 'numeric',
24 | 'value': 0,
25 | 'required': True,
26 | 'displayName': "Fill Value",
27 | 'description': ("")
28 | },
29 | ]
30 |
31 | def updateRasterInfo(self, **kwargs):
32 | b = kwargs['raster_info']['bandCount']
33 | self.fillValue = kwargs.get('value', 0.)
34 | kwargs['output_info']['statistics'] = b * ({'minimum': self.fillValue, 'maximum': self.fillValue}, )
35 | kwargs['output_info']['histogram'] = ()
36 | return kwargs
37 |
38 | def updatePixels(self, tlc, shape, props, **pixelBlocks):
39 | pixelBlocks['output_pixels'] = np.full(shape, self.fillValue, dtype=props['pixelType'])
40 | return pixelBlocks
41 |
--------------------------------------------------------------------------------
/functions/FillRaster.rft.xml:
--------------------------------------------------------------------------------
1 |
2 | FillRaster
3 | A raster function template.
4 |
5 | Fill Raster Function
6 |
7 | UNKNOWN
8 |
9 |
10 |
11 | PythonModule
12 | ClassName
13 | raster
14 | value
15 |
16 |
17 | FillRaster.py
18 | FillRaster
19 |
20 | Raster
21 |
22 |
23 | true
24 |
25 |
26 | value
27 |
28 | 0
29 | false
30 |
31 | @Field.ObjectID
32 |
33 |
34 |
35 |
36 |
37 | 0
38 |
39 |
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/functions/FindMax.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | import datetime
3 |
4 | # For Debugging
5 | import os
6 | import sys
7 | #import pickle
8 |
9 | #debug_logs_directory = r'C:\Users\greg6750\PycharmProjects\ArtPRF'
10 |
11 | '''
12 | This raster function performs nearest neighbors analysis using scikit-learn and then maps the
13 | input values to the resulting neighbor array.
14 |
15 | http://scikit-learn.org/stable/documentation.html
16 | http://scikit-learn.org/stable/modules/generated/sklearn.neighbors.NearestNeighbors.html#sklearn.neighbors.NearestNeighbors
17 | https://docs.scipy.org/doc/numpy/reference/generated/numpy.loadtxt.html#numpy.loadtxt
18 | '''
19 |
20 | class FindMax():
21 | def __init__(self):
22 | self.name = 'FindIndex'
23 | self.description = 'Finds the index of the best raster.'
24 |
25 |
26 |
27 | def getParameterInfo(self):
28 | return [
29 | {
30 | 'name': 'rasters',
31 | 'dataType': 'rasters',
32 | 'value': None,
33 | 'required': True,
34 | 'displayName': 'Input Rasters',
35 | 'description': 'Rasters for analysis.'
36 | }
37 | ]
38 |
39 | def getConfiguration(self, **scalars):
40 | return {
41 | #'inheritProperties': 1 | 2 | 4 | 8, # inherit all from the raster
42 | 'invalidateProperties': 2 | 4 | 8, # reset stats, histogram, key properties
43 | 'resamplingType': 0
44 | }
45 |
46 | def updateRasterInfo(self, **kwargs):
47 |
48 |
49 | # Output pixel information
50 | kwargs['output_info']['pixelType'] = 'f4'
51 | kwargs['output_info']['noData'] = 0
52 | kwargs['output_info']['histogram'] = ()
53 | kwargs['output_info']['bandCount'] = 1
54 | # repeat stats for all output raster bands
55 | kwargs['output_info']['statistics'] = ({'minimum': 0, 'maximum': 25.0}, )
56 |
57 |
58 | return kwargs
59 |
60 | def updatePixels(self, tlc, shape, props, **pixelBlocks):
61 |
62 | fname = '{:%Y_%b_%d_%H_%M_%S}_t.txt'.format(datetime.datetime.now())
63 | #filename = os.path.join(debug_logs_directory, fname)
64 |
65 | # Read pixel blocks
66 | pix_blocks = pixelBlocks['rasters_pixels']
67 |
68 | # Convert pixel blocks to numpy array
69 | pix_array = np.asarray(pix_blocks)
70 | array3d = np.squeeze(pix_array)
71 | array3d[array3d > 100] = -1
72 | #max_val = np.max(array3d, 0)
73 | maxind = np.max(array3d, axis=0)
74 | allzeros = np.max(array3d != -1, 0)
75 | maxind[allzeros == False] = -1
76 |
77 | #pickle_filename = os.path.join(debug_logs_directory, fname)
78 | #pickle.dump(pix_blocks, open(pickle_filename[:-4]+'pix_blocks.p',"wb"))
79 |
80 | # Write output pixels
81 | pixelBlocks['output_pixels'] = maxind.astype(
82 | props['pixelType'],
83 | copy=False
84 | )
85 |
86 | return pixelBlocks
87 |
88 | def updateKeyMetadata(self, names, bandIndex, **keyMetadata):
89 | return keyMetadata
--------------------------------------------------------------------------------
/functions/FindMaxPixel_Mosaic.rft.xml:
--------------------------------------------------------------------------------
1 | FindMaxPixel_MosaicTestFindIndexFinds the index of the best raster.UNKNOWNrastersExtentTypeCellsizeTypePythonModuleClassNamerasters__IsRasterArray__falseExtentTypefalseCellsizeTypefalseC:\Users\greg6750\PycharmProjects\ArtPRF\FindMax.pyClassNameFindMaxfalse__tans__(rasters)2TagCategoryMatchVariableMatchVariable1falseUnionDimensionUnionDimension0false
--------------------------------------------------------------------------------
/functions/FindSecondMax.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | import datetime
3 |
4 | # For Debugging
5 | import os
6 | import sys
7 | #import pickle
8 |
9 | #debug_logs_directory = r'C:\Users\greg6750\PycharmProjects\ArtPRF'
10 |
11 | '''
12 | This raster function performs nearest neighbors analysis using scikit-learn and then maps the
13 | input values to the resulting neighbor array.
14 |
15 | http://scikit-learn.org/stable/documentation.html
16 | http://scikit-learn.org/stable/modules/generated/sklearn.neighbors.NearestNeighbors.html#sklearn.neighbors.NearestNeighbors
17 | https://docs.scipy.org/doc/numpy/reference/generated/numpy.loadtxt.html#numpy.loadtxt
18 | '''
19 |
20 | class FindSecondMax():
21 | def __init__(self):
22 | self.name = 'FindIndex'
23 | self.description = 'Finds the index of the best raster.'
24 |
25 |
26 |
27 | def getParameterInfo(self):
28 | return [
29 | {
30 | 'name': 'rasters',
31 | 'dataType': 'rasters',
32 | 'value': None,
33 | 'required': True,
34 | 'displayName': 'Input Rasters',
35 | 'description': 'Rasters for analysis.'
36 | }
37 | ]
38 |
39 | def getConfiguration(self, **scalars):
40 | return {
41 | #'inheritProperties': 1 | 2 | 4 | 8, # inherit all from the raster
42 | 'invalidateProperties': 2 | 4 | 8, # reset stats, histogram, key properties
43 | 'resamplingType': 0
44 | }
45 |
46 | def updateRasterInfo(self, **kwargs):
47 |
48 |
49 | # Output pixel information
50 | kwargs['output_info']['pixelType'] = 'f4'
51 | kwargs['output_info']['noData'] = 0
52 | kwargs['output_info']['histogram'] = ()
53 | kwargs['output_info']['bandCount'] = 1
54 | # repeat stats for all output raster bands
55 | kwargs['output_info']['statistics'] = ({'minimum': 0, 'maximum': 25.0}, )
56 |
57 |
58 | return kwargs
59 |
60 | def updatePixels(self, tlc, shape, props, **pixelBlocks):
61 |
62 | #fname = '{:%Y_%b_%d_%H_%M_%S}_t.txt'.format(datetime.datetime.now())
63 | #filename = os.path.join(debug_logs_directory, fname)
64 |
65 | # Read pixel blocks
66 | pix_blocks = pixelBlocks['rasters_pixels']
67 |
68 | # Convert pixel blocks to numpy array
69 | pix_array = np.asarray(pix_blocks)
70 | array3d = np.squeeze(pix_array)
71 | array3d[array3d > 100] = -1
72 | #max_val = np.max(array3d, 0)
73 | secondmaxind = np.sort(array3d, axis=0)[-2]
74 | # maxind = np.argmax(array3d, axis=0)
75 | allzeros = np.max(array3d != -1, 0)
76 | secondmaxind[allzeros == False] = -1
77 |
78 | #pickle_filename = os.path.join(debug_logs_directory, fname)
79 | #pickle.dump(pix_blocks, open(pickle_filename[:-4]+'pix_blocks.p',"wb"))
80 |
81 | # Write output pixels
82 | pixelBlocks['output_pixels'] = secondmaxind.astype(
83 | props['pixelType'],
84 | copy=False
85 | )
86 |
87 | return pixelBlocks
88 |
89 | def updateKeyMetadata(self, names, bandIndex, **keyMetadata):
90 | return keyMetadata
--------------------------------------------------------------------------------
/functions/FindSecondMaxPixel_Mosaic.rft.xml:
--------------------------------------------------------------------------------
1 | FindSecondMaxPixel_MosaicasdasdFindIndexFinds the index of the best raster.UNKNOWNrastersExtentTypeCellsizeTypePythonModuleClassNamerasters__IsRasterArray__falseExtentTypefalseCellsizeTypefalseC:\Users\greg6750\PycharmProjects\ArtPRF\FindSecondMax.pyClassNameFindSecondMaxfalse__tans__(rasters)2TagCategoryMatchVariableMatchVariable1falseUnionDimensionUnionDimension0false
--------------------------------------------------------------------------------
/functions/FindThirdMax.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | import datetime
3 |
4 | # For Debugging
5 | import os
6 | import sys
7 | #import pickle
8 |
9 | #debug_logs_directory = r'C:\Users\greg6750\PycharmProjects\ArtPRF'
10 |
11 | '''
12 | This raster function performs nearest neighbors analysis using scikit-learn and then maps the
13 | input values to the resulting neighbor array.
14 |
15 | http://scikit-learn.org/stable/documentation.html
16 | http://scikit-learn.org/stable/modules/generated/sklearn.neighbors.NearestNeighbors.html#sklearn.neighbors.NearestNeighbors
17 | https://docs.scipy.org/doc/numpy/reference/generated/numpy.loadtxt.html#numpy.loadtxt
18 | '''
19 |
20 | class FindThirdMax():
21 | def __init__(self):
22 | self.name = 'FindIndex'
23 | self.description = 'Finds the index of the best raster.'
24 |
25 |
26 |
27 | def getParameterInfo(self):
28 | return [
29 | {
30 | 'name': 'rasters',
31 | 'dataType': 'rasters',
32 | 'value': None,
33 | 'required': True,
34 | 'displayName': 'Input Rasters',
35 | 'description': 'Rasters for analysis.'
36 | }
37 | ]
38 |
39 | def getConfiguration(self, **scalars):
40 | return {
41 | #'inheritProperties': 1 | 2 | 4 | 8, # inherit all from the raster
42 | 'invalidateProperties': 2 | 4 | 8, # reset stats, histogram, key properties
43 | 'resamplingType': 0
44 | }
45 |
46 | def updateRasterInfo(self, **kwargs):
47 |
48 |
49 | # Output pixel information
50 | kwargs['output_info']['pixelType'] = 'f4'
51 | kwargs['output_info']['noData'] = 0
52 | kwargs['output_info']['histogram'] = ()
53 | kwargs['output_info']['bandCount'] = 1
54 | # repeat stats for all output raster bands
55 | kwargs['output_info']['statistics'] = ({'minimum': 0, 'maximum': 25.0}, )
56 |
57 |
58 | return kwargs
59 |
60 | def updatePixels(self, tlc, shape, props, **pixelBlocks):
61 |
62 | #fname = '{:%Y_%b_%d_%H_%M_%S}_t.txt'.format(datetime.datetime.now())
63 | #filename = os.path.join(debug_logs_directory, fname)
64 |
65 | # Read pixel blocks
66 | pix_blocks = pixelBlocks['rasters_pixels']
67 |
68 | # Convert pixel blocks to numpy array
69 | pix_array = np.asarray(pix_blocks)
70 | array3d = np.squeeze(pix_array)
71 | array3d[array3d > 100] = -1
72 | #max_val = np.max(array3d, 0)
73 | thirdmaxind = np.sort(array3d, axis=0)[-3]
74 | # maxind = np.argmax(array3d, axis=0)
75 | allzeros = np.max(array3d != -1, 0)
76 | thirdmaxind[allzeros == False] = -1
77 |
78 | #pickle_filename = os.path.join(debug_logs_directory, fname)
79 | #pickle.dump(pix_blocks, open(pickle_filename[:-4]+'pix_blocks.p',"wb"))
80 |
81 | # Write output pixels
82 | pixelBlocks['output_pixels'] = thirdmaxind.astype(
83 | props['pixelType'],
84 | copy=False
85 | )
86 |
87 | return pixelBlocks
88 |
89 | def updateKeyMetadata(self, names, bandIndex, **keyMetadata):
90 | return keyMetadata
--------------------------------------------------------------------------------
/functions/FindThirdMaxPixel_Mosaic.rft.xml:
--------------------------------------------------------------------------------
1 | FindThirdMaxPixel_MosaicadasdaFindIndexFinds the index of the best raster.UNKNOWNrastersExtentTypeCellsizeTypePythonModuleClassNamerasters__IsRasterArray__falseExtentTypefalseCellsizeTypefalseC:\Users\greg6750\PycharmProjects\ArtPRF\FindThirdMax.pyClassNameFindThirdMaxfalse__tans__(rasters)2TagCategoryMatchVariableMatchVariable1falseUnionDimensionUnionDimension0false
--------------------------------------------------------------------------------
/functions/FishHabitatSuitability.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 |
3 |
4 | class FishHabitatSuitability():
5 |
6 | def __init__(self):
7 | self.name = "Fish Habitat Suitability Function"
8 | self.description = "Computes fish habitat suitability by depth."
9 | self.depth = 0.0
10 |
11 | def getParameterInfo(self):
12 | return [
13 | {
14 | 'name': 'temperature',
15 | 'dataType': 'raster',
16 | 'value': None,
17 | 'required': True,
18 | 'displayname': "Surface Temperature Raster",
19 | 'description': "A single-band raster where values represent surface temperature in Celsius.",
20 | },
21 | {
22 | 'name': 'salinity',
23 | 'dataType': 'raster',
24 | 'value': None,
25 | 'required': True,
26 | 'displayname': "Surface Salinty Raster",
27 | 'description': "A single-band raster where values represent surface salinity in PSU.",
28 | },
29 | {
30 | 'name': 'depth',
31 | 'dataType': 'numeric',
32 | 'value': self.depth,
33 | 'required': True,
34 | 'displayname': "Ocean Depth",
35 | 'description': "A numeric value representing ocean depth in meters.",
36 | },
37 | ]
38 |
39 | def getConfiguration(self, **scalars):
40 | return {
41 | 'inheritProperties': 2 | 4 | 8, # inherit everything but the pixel type (1)
42 | 'invalidateProperties': 2 | 4 | 8 # invalidate these aspects because we are modifying pixels and key metadata
43 | }
44 |
45 | def updateRasterInfo(self, **kwargs):
46 | kwargs['output_info']['bandCount'] = 1
47 | kwargs['output_info']['pixelType'] = 'f4'
48 | kwargs['output_info']['statistics'] = ({'minimum': 0.0, 'maximum': 1.0}, )
49 | kwargs['output_info']['histogram'] = ()
50 | self.depth = abs(float(kwargs['depth']))
51 |
52 | # piece-wise linear parameters for depth...
53 | d = self.depth
54 | dMinA = 0
55 | dMinP = 2
56 | dMaxP = 11
57 | dMaxA = 20
58 |
59 | if d < dMinA or d > dMaxA:
60 | d = 0.0
61 | elif d <= dMinP:
62 | d = (d - dMinA) / (dMinP - dMinA)
63 | elif d >= dMaxP:
64 | d = (d - dMaxA) / (dMaxP - dMaxA)
65 | else:
66 | d = 1
67 |
68 | self.depth = d
69 | return kwargs
70 |
71 | def updatePixels(self, tlc, shape, props, **pixelBlocks):
72 | t = np.array(pixelBlocks['temperature_pixels'], dtype='f4', copy=False)
73 | s = np.array(pixelBlocks['salinity_pixels'], dtype='f4', copy=False)
74 |
75 | # piece-wise linear parameters for temperature...
76 | tMinA = 17.99
77 | tMinP = 26.37
78 | tMaxP = 29.15
79 | tMaxA = 33.35
80 |
81 | np.putmask(t, t <= tMinP, (t - tMinA) / (tMinP - tMinA))
82 | np.putmask(t, t >= tMaxP, (t - tMaxA) / (tMaxP - tMaxA))
83 | np.putmask(t, (t > tMinP) & (t < tMaxP), 1)
84 | np.putmask(t, t < 0, 0)
85 |
86 | # piece-wise linear parameters for salinity...
87 | sMinA = 28.81
88 | sMinP = 32.27
89 | sMaxP = 35.81
90 | sMaxA = 36.79
91 |
92 | np.putmask(s, s <= sMinP, (s - sMinA) / (sMinP - sMinA))
93 | np.putmask(s, s >= sMaxP, (s - sMaxA) / (sMaxP - sMaxA))
94 | np.putmask(s, (s > sMinP) & (s < sMaxP), 1)
95 | np.putmask(s, s < 0, 0)
96 |
97 | # get overall probability by tying all conditions
98 | pixelBlocks['output_pixels'] = np.array(t * s * self.depth).astype(props['pixelType'], copy=False)
99 | return pixelBlocks
100 |
101 | def updateKeyMetadata(self, names, bandIndex, **keyMetadata):
102 | if bandIndex == -1:
103 | keyMetadata['datatype'] = 'Scientific'
104 | keyMetadata['variable'] = 'FishHabitatSuitability'
105 | return keyMetadata
106 |
--------------------------------------------------------------------------------
/functions/FishHabitatSuitability.rft.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Fish Habitat Suitability
4 | A raster function template.
5 |
6 | Python Adapter Function
7 | Adapter function for raster functions written in python.
8 | UNKNOWN
9 |
10 |
11 |
12 | PythonModule
13 | temperature
14 | salinity
15 | depth
16 |
17 |
18 | FishHabitatSuitability.py
19 |
20 | water_temp
21 | A single-band raster where values represent surface temperature in Celsius.
22 |
23 | true
24 |
25 | Raster1
26 |
27 |
28 |
29 | salinity
30 | A single-band raster where values represent surface salinity in PSU.
31 |
32 | true
33 |
34 | Raster2
35 |
36 |
37 |
38 | depth
39 | A numeric value representing ocean depth in meters.
40 |
41 | false
42 |
43 | @Field.StdZ
44 |
45 |
46 |
47 |
48 |
49 | 2
50 |
51 |
52 | GroupName
53 | Tag
54 |
55 |
--------------------------------------------------------------------------------
/functions/GradientBoostedClassifier.py:
--------------------------------------------------------------------------------
1 | import pandas as pd
2 | from sklearn.ensemble import GradientBoostingClassifier
3 | import numpy as np
4 |
5 | '''
6 | Gradient Boosting for classification.
7 |
8 | GB builds an additive model in a forward stage-wise fashion;
9 | it allows for the optimization of arbitrary differentiable loss functions.
10 | In each stage n_classes_ regression trees are fit on the negative gradient of the binomial or
11 | multinomial deviance loss function.
12 | Binary classification is a special case where only a single regression tree is induced.
13 |
14 | https://scikit-learn.org/stable/modules/generated/sklearn.ensemble.GradientBoostingClassifier.html?
15 | highlight=gradient#sklearn.ensemble.GradientBoostingClassifier
16 | '''
17 |
18 | # Note: Can not name the class GradientBoostedClassifier because it will conflict
19 | # with the scikit-learn class that is imported.
20 | class BoostedClassifier():
21 | def __init__(self):
22 | self.name = 'Gradient Boosting Classifier (Boosted Regression Trees)'
23 | self.description = 'Gradient Boosting Classifier from scikit-learn ' \
24 | 'implemented as a Python Raster Function'
25 |
26 | # inputs as string, but eventually will be numpy arrays
27 | self.df = None
28 | self.datafile = None
29 | self.threshold = 0.5
30 |
31 |
32 | def getParameterInfo(self):
33 | return [
34 | {
35 | 'name': 'rasters',
36 | 'dataType': 'rasters',
37 | 'value': None,
38 | 'required': True,
39 | 'displayName': 'Input Rasters',
40 | 'description': 'Must be in the same order as the columns in the CSV file'
41 | },
42 | {
43 | 'name': 'training_data_from_file',
44 | 'dataType': 'string',
45 | 'value': 'C:\\PROJECTS\\ML\\training_data.csv',
46 | 'required': True,
47 | 'displayName': 'Training data CSV filepath',
48 | 'description': 'Full filepath directory to training data CSV. '
49 | 'Internally this will load from disk and be converted to a pandas dataframe.'
50 | }
51 | ]
52 |
53 | def getConfiguration(self, **scalars):
54 | return {
55 | 'inheritProperties': 1 | 2 | 4 | 8, # inherit all from the raster
56 | 'invalidateProperties': 2 | 4 | 8 # reset stats, histogram, key properties
57 | }
58 |
59 | def updateRasterInfo(self, **kwargs):
60 |
61 | # convert filepath string input param to numpy array
62 | self.datafile = str(kwargs['training_data_from_file'])
63 | #self.threshold = float(kwargs['threshold'])
64 |
65 | kwargs['output_info']['pixelType'] = 'f4'
66 | kwargs['output_info']['histogram'] = ()
67 | kwargs['output_info']['statistics'] = ()
68 | kwargs['output_info']['bandCount'] = 3
69 |
70 | return kwargs
71 |
72 | def updatePixels(self, tlc, shape, props, **pixelBlocks):
73 |
74 |
75 | self.df = pd.read_csv(self.datafile)
76 |
77 | try:
78 | fields_to_drop = ['OBJECTID', 'LOCATION_X', 'LOCATION_Y']
79 | self.df.drop(fields_to_drop, axis=1, inplace=True)
80 | except:
81 | pass
82 |
83 | y_val = 'VarToPredict'
84 | x_train = self.df.loc[:, self.df.columns != y_val]
85 | y_train = self.df[y_val]
86 | x_train.fillna(0, inplace=True)
87 | y_train.fillna(0, inplace=True)
88 |
89 | # Initialize RandomForestClassifier
90 | # Recommend trying different values for:
91 | # - n_estimators
92 | # - learning_rate
93 | # - max_depth
94 | # - random_state
95 | regr1 = GradientBoostingClassifier(n_estimators=100, learning_rate=1.0,
96 | max_depth=3, random_state=0)
97 | regr1.fit(x_train, y_train)
98 |
99 | pix_blocks = pixelBlocks['rasters_pixels']
100 | pix_array = np.asarray(pix_blocks)
101 | pix_array = np.squeeze(pix_array)
102 |
103 | pixels_reshaped = pix_array.reshape(pix_array.shape[0], -1).transpose()
104 |
105 | # Run RandomForestRegressor
106 | pred = regr1.predict(pixels_reshaped)
107 | pred_proba = regr1.predict_proba(pixels_reshaped)
108 |
109 | res = np.hstack([np.expand_dims(pred, 1), pred_proba])
110 |
111 | #res = pred.reshape((pix_array.shape[1], pix_array.shape[2]))
112 | res_reshape = np.reshape(
113 | res.transpose(),
114 | (3, pix_array.shape[1], pix_array.shape[2])
115 | )
116 |
117 | res_reshape[res_reshape <= self.threshold] = 0.0
118 |
119 | #res[res <= self.threshold] = 0
120 | # remember that self.n_neighbors is the desired out band count
121 | #res = np.zeros(
122 | # (pix_array.shape[1], pix_array.shape[2])
123 | #)
124 |
125 | pixelBlocks['output_pixels'] = res_reshape.astype(
126 | props['pixelType'],
127 | copy=True
128 | )
129 |
130 | return pixelBlocks
131 |
132 | def updateKeyMetadata(self, names, bandIndex, **keyMetadata):
133 | return keyMetadata
134 |
--------------------------------------------------------------------------------
/functions/HexagonPixels.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | import math
3 |
4 | class HexagonPixels():
5 |
6 | def __init__(self):
7 | self.name = "Hexagon Pixels"
8 | self.description = ("Creates a DEM raster of hexagons.")
9 |
10 | def getParameterInfo(self):
11 | return [
12 | {
13 | 'name': 'dem',
14 | 'dataType': 'raster',
15 | 'value': None,
16 | 'required': True,
17 | 'displayName': "DEM Raster",
18 | 'description': "The digital elevation model (DEM)."
19 | }
20 | ]
21 |
22 | def getConfiguration(self, **scalars):
23 | return {
24 | 'compositeRasters': False,
25 | 'inheritProperties': 1 | 2 | 4 | 8, # inherit all from the raster
26 | 'invalidateProperties': 2 | 4 | 8, # reset stats, histogram, key properties
27 | 'inputMask': False
28 | }
29 |
30 | def updateRasterInfo(self, **kwargs):
31 | # repeat stats for all output raster bands
32 | kwargs['output_info']['bandCount'] = 1
33 | kwargs['output_info']['histogram'] = () # reset histogram
34 | kwargs['output_info']['pixelType'] = 'u1'
35 | kwargs['output_info']['noData'] = np.array([0], 'u1')
36 |
37 | return kwargs
38 |
39 | def updatePixels(self, tlc, shape, props, **pixelBlocks):
40 | # get the input DEM raster pixel block
41 | inBlock_dem = pixelBlocks['dem_pixels']
42 | hex_pixels = np.zeros(inBlock_dem.shape)
43 | x, y = inBlock_dem.shape
44 | x_pix_size = 9 #8
45 | y_pix_size = 7
46 | maximum = np.max(inBlock_dem)
47 | minimum = np.min(inBlock_dem)
48 | spread = maximum-minimum
49 | num_squares_x = math.floor(x/x_pix_size)
50 | num_squares_y = math.floor(y/y_pix_size)
51 |
52 | #Loop 1 te generate first hexagons
53 | for num_x in range(1,int(num_squares_x)):
54 | for num_y in range(1,int(num_squares_y)):
55 |
56 | pix = np.mean(inBlock_dem[(num_x-1)*x_pix_size:(num_x-1)*x_pix_size+8, (num_y-1)*y_pix_size:(num_y-1)*y_pix_size+6])
57 |
58 | hex_pixels[(num_x-1)*x_pix_size+3:(num_x-1)*x_pix_size+6,(num_y-1)*y_pix_size+0] = pix
59 | hex_pixels[(num_x-1)*x_pix_size+2:(num_x-1)*x_pix_size+7,(num_y-1)*y_pix_size+1] = pix
60 | hex_pixels[(num_x-1)*x_pix_size+1:(num_x-1)*x_pix_size+8,(num_y-1)*y_pix_size+2] = pix
61 | hex_pixels[(num_x-1)*x_pix_size+0:(num_x-1)*x_pix_size+9,(num_y-1)*y_pix_size+3] = pix
62 | hex_pixels[(num_x-1)*x_pix_size+1:(num_x-1)*x_pix_size+8,(num_y-1)*y_pix_size+4] = pix
63 | hex_pixels[(num_x-1)*x_pix_size+2:(num_x-1)*x_pix_size+7,(num_y-1)*y_pix_size+5] = pix
64 | hex_pixels[(num_x-1)*x_pix_size+3:(num_x-1)*x_pix_size+6,(num_y-1)*y_pix_size+6] = pix
65 |
66 | #Loop 2 to overlay the hexagons on top of the result of loop 1
67 | for num_x in range(1,int(num_squares_x)):
68 | for num_y in range(1,int(num_squares_y)):
69 |
70 | pix = np.mean(inBlock_dem[(num_x-1)*x_pix_size+4:(num_x-1)*x_pix_size+8+4, (num_y-1)*y_pix_size+3:(num_y-1)*y_pix_size+6+3])
71 |
72 | hex_pixels[(num_x-1)*x_pix_size+3+4:(num_x-1)*x_pix_size+6+4,(num_y-1)*y_pix_size+0+3] = pix
73 | hex_pixels[(num_x-1)*x_pix_size+2+4:(num_x-1)*x_pix_size+7+4,(num_y-1)*y_pix_size+1+3] = pix
74 | hex_pixels[(num_x-1)*x_pix_size+1+4:(num_x-1)*x_pix_size+8+4,(num_y-1)*y_pix_size+2+3] = pix
75 | hex_pixels[(num_x-1)*x_pix_size+0+4:(num_x-1)*x_pix_size+9+4,(num_y-1)*y_pix_size+3+3] = pix
76 | hex_pixels[(num_x-1)*x_pix_size+1+4:(num_x-1)*x_pix_size+8+4,(num_y-1)*y_pix_size+4+3] = pix
77 | hex_pixels[(num_x-1)*x_pix_size+2+4:(num_x-1)*x_pix_size+7+4,(num_y-1)*y_pix_size+5+3] = pix
78 | hex_pixels[(num_x-1)*x_pix_size+3+4:(num_x-1)*x_pix_size+6+4,(num_y-1)*y_pix_size+6+3] = pix
79 |
80 | # format output pixels
81 | outBlocks = hex_pixels.astype(props['pixelType'], copy=False)
82 | pixelBlocks['output_pixels'] = outBlocks
83 | return pixelBlocks
84 |
85 | def updateKeyMetadata(self, names, bandIndex, **keyMetadata):
86 | if bandIndex == -1: # dataset level
87 | keyMetadata['datatype'] = 'Scientific'
88 | else: # output "band"
89 | keyMetadata['wavelengthmin'] = None
90 | keyMetadata['wavelengthmax'] = None
91 | keyMetadata['bandname'] = 'GIS is Art'
92 | return keyMetadata
93 |
94 |
95 |
96 |
--------------------------------------------------------------------------------
/functions/KNearestNeighborsClassifier.py:
--------------------------------------------------------------------------------
1 | import pandas as pd
2 | import numpy as np
3 | from sklearn.neighbors import KNeighborsClassifier
4 |
5 |
6 | '''
7 | This raster function performs k-nearest neighbors classification
8 | using scikit-learn and generates a map of the classification of
9 | each pixel.
10 |
11 | http://scikit-learn.org/stable/documentation.html
12 | http://scikit-learn.org/stable/modules/generated/sklearn.neighbors.NearestNeighbors.html#sklearn.neighbors.NearestNeighbors
13 | https://docs.scipy.org/doc/numpy/reference/generated/numpy.loadtxt.html#numpy.loadtxt
14 | '''
15 |
16 | class KNNClassifier():
17 | def __init__(self):
18 | self.name = 'K-Nearest Neighbor Classifier'
19 | self.description = 'This raster function performs k-nearest neighbors classification' \
20 | ' using scikit-learn and generates a map of the classification of' \
21 | ' each pixel.'
22 |
23 | # The number of neighbors to use
24 | self.n_neighbors = None
25 |
26 | # The CSV file containing the training data
27 | # The inputs is a string
28 | self.training_data_from_file = None
29 |
30 | def getParameterInfo(self):
31 | return [
32 | {
33 | 'name': 'rasters',
34 | 'dataType': 'rasters',
35 | 'value': None,
36 | 'required': True,
37 | 'displayName': 'Input Rasters',
38 | 'description': 'This should include several individual rasters. The rasters must be in the same order as the columns in the CSV file'
39 | },
40 | {
41 | 'name': 'n_neighbors',
42 | 'dataType': 'numeric',
43 | 'value': 5,
44 | 'required': True,
45 | 'displayName': 'Number of neighbors (integer)',
46 | 'description': 'Number of neighbors to use by default for kneighbors queries (integer).'
47 | },
48 | {
49 | 'name': 'training_data_from_file',
50 | 'dataType': 'string',
51 | 'value': 'C:\\PROJECTS\\ML\\training_data.csv',
52 | 'required': True,
53 | 'displayName': 'Training data CSV filepath',
54 | 'description': 'Full filepath directory to training data CSV. '
55 | 'Internally this will load from disk and be converted to a pandas dataframe.'
56 | }
57 | ]
58 |
59 | def getConfiguration(self, **scalars):
60 | return {
61 | 'inheritProperties': 1 | 2 | 4 | 8, # inherit all from the raster
62 | 'invalidateProperties': 2 | 4 | 8 # reset stats, histogram, key properties
63 | }
64 |
65 | def updateRasterInfo(self, **kwargs):
66 | self.n_neighbors = int(kwargs['n_neighbors'])
67 | self.datafile = str(kwargs['training_data_from_file'])
68 |
69 | # Number of output bands:
70 | # There should be one band for each neighbor calculated
71 | #out_band_count = self.n_neighbors
72 |
73 | # Output pixel information
74 | kwargs['output_info']['pixelType'] = 'f4'
75 | kwargs['output_info']['statistics'] = ()
76 | kwargs['output_info']['histogram'] = ()
77 | kwargs['output_info']['bandCount'] = 1
78 |
79 | return kwargs
80 |
81 | def updatePixels(self, tlc, shape, props, **pixelBlocks):
82 |
83 | # Read the input CSV file into a dataframe
84 | self.df = pd.read_csv(self.datafile)#(datafile)
85 |
86 | # Drop the fields that are not involved in predicting or mapping the values.
87 | # These fields will generally be things like "OBJECTID".
88 | fields_to_drop = ['OBJECTID', 'LOCATION_X', 'LOCATION_Y'] # Fields that aren't used in the analysis
89 | self.df.drop(fields_to_drop, axis=1, inplace=True)
90 |
91 | # Separate dataframe into training environmental variables and observed values.
92 | # The environmental values, x_train, are used to train the model
93 | # We are trying to map\predict the y_train value
94 | y_val = 'VarToPredict' # The value that you probably want to predict
95 | x_train = self.df.loc[:, self.df.columns != y_val]
96 | y_train = self.df[y_val]
97 |
98 | # The model won't work if there is missing or null data.
99 | # Fill null values with 0 (or some other value)
100 | x_train.fillna(0, inplace=True)
101 | y_train.fillna(0, inplace=True)
102 |
103 | # Read pixel blocks
104 | pix_blocks = pixelBlocks['rasters_pixels']
105 |
106 | # Convert pixel blocks to numpy array
107 | pix_array = np.asarray(pix_blocks)
108 |
109 | # Remove any extra indices
110 | pix_array = np.squeeze(pix_array)
111 |
112 | # Reshape the pixels into a 2D array that is number of pixels x number of predictor variables
113 | pixels_reshaped = pix_array.reshape(pix_array.shape[0], -1).transpose()
114 |
115 | # Classify each point using the k-neighbors classifier
116 | knn = KNeighborsClassifier(n_neighbors=self.n_neighbors)
117 | pred = knn.fit(x_train, y_train).predict(pixels_reshaped)
118 |
119 | # Reshape into a 2D array
120 | res = pred.reshape((pix_array.shape[1], pix_array.shape[2]))
121 |
122 | # Write output pixels
123 | pixelBlocks['output_pixels'] = res.astype(
124 | props['pixelType'],
125 | copy=True
126 | )
127 |
128 | return pixelBlocks
129 |
130 | def updateKeyMetadata(self, names, bandIndex, **keyMetadata):
131 | return keyMetadata
132 |
--------------------------------------------------------------------------------
/functions/Landsat ETM Pixel Percentile.rft.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Landsat ETM Pixel Percentile
4 | Creates a Landsat scene from the 50th percentile pixel at each location given a collection of images that were taken between the start day and year and the end day and year.
5 |
6 | Landsat Pixel Percentile
7 | This function creates a synthetic Landsat image given a day of year rangeand the percentile of the pixel that we want to calculate.
8 | UNKNOWN
9 |
10 |
11 |
12 | rasters
13 | sensor
14 | percentile
15 | start_day
16 | start_year
17 | end_day
18 | end_year
19 | PythonModule
20 | ClassName
21 |
22 |
23 |
24 | rasters
25 |
26 |
27 |
28 | __IsRasterArray__
29 |
30 | false
31 |
32 |
33 | sensor
34 |
35 | Landsat ETM
36 | false
37 |
38 |
39 | percentile
40 |
41 | 50
42 | false
43 |
44 |
45 | start_day
46 |
47 | 120
48 | false
49 |
50 |
51 | start_year
52 |
53 | 2005
54 | false
55 |
56 |
57 | end_day
58 |
59 | 240
60 | false
61 |
62 |
63 | end_year
64 |
65 | 2012
66 | false
67 |
68 | C:\PROJECTS\gbrunner-raster-functions\functions\LandsatPixelPercentile.py
69 |
70 | ClassName
71 |
72 | LandsatPixelPercentile
73 | false
74 |
75 |
76 |
77 |
78 | __tans__(rasters,sensor,percentile,start_day,start_year,end_day,end_year)
79 |
80 |
81 | 2
82 |
83 |
84 | Tag
85 | Name
86 |
87 |
88 |
--------------------------------------------------------------------------------
/functions/Landsat ETM Scene Synthesis.rft.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Landsat ETM Scene Synthesis
4 | Creates a Landsat ETM scene for a user defined month by taking the mean pixel value at that location for that month.
5 |
6 | Landsat Scene Synthesis
7 | This function takes as input a spatial and temporal mosaic dataset of Landsat images, selects images for user defined month, filters out cloudy pixels from each image in the stack, then averages the values along a spatial element to create a synthetic Landsat image for the user defined month.
8 | UNKNOWN
9 |
10 |
11 |
12 | rasters
13 | sensor
14 | predict_month
15 | PythonModule
16 | ClassName
17 |
18 |
19 |
20 | rasters
21 |
22 |
23 |
24 | __IsRasterArray__
25 |
26 | false
27 |
28 |
29 | sensor
30 |
31 | Landsat ETM
32 | false
33 |
34 |
35 | predict_month
36 |
37 | Jan
38 | false
39 |
40 | C:\PROJECTS\gbrunner-raster-functions\functions\LandsatImageSynthesis.py
41 |
42 | ClassName
43 |
44 | LandsatImageSynthesis
45 | false
46 |
47 |
48 |
49 |
50 | __tans__(rasters,sensor,predict_month)
51 |
52 |
53 | 2
54 |
55 |
56 | Tag
57 | Name
58 |
59 |
--------------------------------------------------------------------------------
/functions/Landsat Image Synthesis.rft.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Landsat Image Synthesis
4 | Landsat Scene Generation Raster Function
5 |
6 | Landsat 5 Scene Synthesis
7 | Landsat 5 Scene Synthesis
8 | UNKNOWN
9 |
10 |
11 |
12 | rasters
13 | predict_month
14 | PythonModule
15 | ClassName
16 |
17 |
18 |
19 | rasters
20 |
21 |
22 |
23 | __IsRasterArray__
24 |
25 | false
26 |
27 |
28 | predict_month
29 |
30 | Mar
31 | false
32 |
33 | Landsat_Image_Synthesis.py
34 |
35 | ClassName
36 |
37 | Landsat_Image_Synthesis
38 | false
39 |
40 |
41 |
42 |
43 | __tans__(rasters,predict_month)
44 |
45 |
46 | 2
47 |
48 |
49 | Tag
50 | Name
51 |
52 |
53 |
--------------------------------------------------------------------------------
/functions/Landsat OLI Pixel Percentile.rft.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Landsat OLI Pixel Percentile
4 | Creates a Landsat OLI image from the 50th percentile pixel at each location for a collection of images taken between the start day and year and the end day and year.
5 |
6 | Landsat Pixel Percentile
7 | This function creates a synthetic Landsat image given a day of year rangeand the percentile of the pixel that we want to calculate.
8 | UNKNOWN
9 |
10 |
11 |
12 | rasters
13 | sensor
14 | percentile
15 | start_day
16 | start_year
17 | end_day
18 | end_year
19 | PythonModule
20 | ClassName
21 |
22 |
23 |
24 | rasters
25 |
26 |
27 |
28 | __IsRasterArray__
29 |
30 | false
31 |
32 |
33 | sensor
34 |
35 | Landsat OLI
36 | false
37 |
38 |
39 | percentile
40 |
41 | 50
42 | false
43 |
44 |
45 | start_day
46 |
47 | 120
48 | false
49 |
50 |
51 | start_year
52 |
53 | 2014
54 | false
55 |
56 |
57 | end_day
58 |
59 | 240
60 | false
61 |
62 |
63 | end_year
64 |
65 | 2017
66 | false
67 |
68 | C:\PROJECTS\gbrunner-raster-functions\functions\LandsatPixelPercentile.py
69 |
70 | ClassName
71 |
72 | LandsatPixelPercentile
73 | false
74 |
75 |
76 |
77 |
78 | __tans__(rasters,sensor,percentile,start_day,start_year,end_day,end_year)
79 |
80 |
81 | 2
82 |
83 |
84 | Tag
85 | Name
86 |
87 |
--------------------------------------------------------------------------------
/functions/Landsat OLI Scene Synthesis.rft.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Landsat OLI Scene Synthesis
4 | Creates a Landsat OLI scene for a user defined month by taking the mean pixel value at that location for that month.
5 |
6 | Landsat Scene Synthesis
7 | This function takes as input a spatial and temporal mosaic dataset of Landsat images, selects images for user defined month, filters out cloudy pixels from each image in the stack, then averages the values along a spatial element to create a synthetic Landsat image for the user defined month.
8 | UNKNOWN
9 |
10 |
11 |
12 | rasters
13 | sensor
14 | predict_month
15 | PythonModule
16 | ClassName
17 |
18 |
19 |
20 | rasters
21 |
22 |
23 |
24 | __IsRasterArray__
25 |
26 | false
27 |
28 |
29 | sensor
30 |
31 | Landsat OLI
32 | false
33 |
34 |
35 | predict_month
36 |
37 | Jan
38 | false
39 |
40 | C:\PROJECTS\gbrunner-raster-functions\functions\LandsatImageSynthesis.py
41 |
42 | ClassName
43 |
44 | LandsatImageSynthesis
45 | false
46 |
47 |
48 |
49 |
50 | __tans__(rasters,sensor,predict_month)
51 |
52 |
53 | 2
54 |
55 |
56 | Tag
57 | Name
58 |
59 |
--------------------------------------------------------------------------------
/functions/Landsat TM Pixel Percentile.rft.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Landsat TM Pixel Percentile
4 | Creates a 50th percentile image from a collection of Landsat TM images taken between the start day and year and the end day and year.
5 |
6 | Landsat Pixel Percentile
7 | This function creates a synthetic Landsat image given a day of year rangeand the percentile of the pixel that we want to calculate.
8 | UNKNOWN
9 |
10 |
11 |
12 | rasters
13 | sensor
14 | percentile
15 | start_day
16 | start_year
17 | end_day
18 | end_year
19 | PythonModule
20 | ClassName
21 |
22 |
23 |
24 | rasters
25 |
26 |
27 |
28 | __IsRasterArray__
29 |
30 | false
31 |
32 |
33 | sensor
34 |
35 | Landsat TM
36 | false
37 |
38 |
39 | percentile
40 |
41 | 50
42 | false
43 |
44 |
45 | start_day
46 |
47 | 120
48 | false
49 |
50 |
51 | start_year
52 |
53 | 1985
54 | false
55 |
56 |
57 | end_day
58 |
59 | 240
60 | false
61 |
62 |
63 | end_year
64 |
65 | 2010
66 | false
67 |
68 | C:\PROJECTS\gbrunner-raster-functions\functions\LandsatPixelPercentile.py
69 |
70 | ClassName
71 |
72 | LandsatPixelPercentile
73 | false
74 |
75 |
76 |
77 |
78 | __tans__(rasters,sensor,percentile,start_day,start_year,end_day,end_year)
79 |
80 |
81 | 2
82 |
83 |
84 | Tag
85 | Name
86 |
87 |
--------------------------------------------------------------------------------
/functions/Landsat TM Scene Synthesis.rft.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Landsat TM Scene Synthesis
4 | Creates a Landsat TM scene for a user defined month by taking the mean pixel value at that location for that month.
5 |
6 | Landsat Scene Synthesis
7 | This function takes as input a spatial and temporal mosaic dataset of Landsat images, selects images for user defined month, filters out cloudy pixels from each image in the stack, then averages the values along a spatial element to create a synthetic Landsat image for the user defined month.
8 | UNKNOWN
9 |
10 |
11 |
12 | rasters
13 | sensor
14 | predict_month
15 | PythonModule
16 | ClassName
17 |
18 |
19 |
20 | rasters
21 |
22 |
23 |
24 | __IsRasterArray__
25 |
26 | false
27 |
28 |
29 | sensor
30 |
31 | Landsat TM
32 | false
33 |
34 |
35 | predict_month
36 |
37 | Jan
38 | false
39 |
40 | C:\PROJECTS\gbrunner-raster-functions\functions\LandsatImageSynthesis.py
41 |
42 | ClassName
43 |
44 | LandsatImageSynthesis
45 | false
46 |
47 |
48 |
49 |
50 | __tans__(rasters,sensor,predict_month)
51 |
52 |
53 | 2
54 |
55 |
56 | Tag
57 | Name
58 |
59 |
--------------------------------------------------------------------------------
/functions/LandsatC2QA.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 |
3 | class LandsatC2QA():
4 |
5 | def __init__(self):
6 | self.name = "Landsat Collection 2 QA Mask"
7 | self.description = "This function creates masks based on Landsat Collection 2 QA band. QA bit index is taken from https://docs.digitalearthafrica.org/en/latest/data_specs/Landsat_C2_SR_specs.html#Quality-assessment-bands"
8 | self.bit_index = {'fill': 0, 'diluted': 1, 'cirrus': 2, 'cloud': 3, 'shadow': 4, 'snow': 5, 'clear': 6, 'water': 7}
9 |
10 | def getParameterInfo(self):
11 | return [
12 | {
13 | 'name': 'r',
14 | 'dataType': 'raster',
15 | 'value': None,
16 | 'required': True,
17 | 'displayName': "Input Landsat QA band",
18 | 'description': "The input QA raster."
19 | },
20 | {
21 | 'name': 'fill',
22 | 'dataType': 'boolean',
23 | 'value': False,
24 | 'required': False,
25 | 'displayName': "Mask fill data",
26 | 'description': "Set fill data pixels to 1"
27 | },
28 | {
29 | 'name': 'diluted',
30 | 'dataType': 'boolean',
31 | 'value': False,
32 | 'required': False,
33 | 'displayName': "Mask dilated cloud",
34 | 'description': "Set dilated cloud pixels to 1"
35 | },
36 | {
37 | 'name': 'cirrus',
38 | 'dataType': 'boolean',
39 | 'value': False,
40 | 'required': False,
41 | 'displayName': "Mask cirrus cloud",
42 | 'description': "Set cirrus cloud pixels to 1"
43 | },
44 | {
45 | 'name': 'cloud',
46 | 'dataType': 'boolean',
47 | 'value': False,
48 | 'required': False,
49 | 'displayName': "Mask cloud",
50 | 'description': "Set cloud pixels to 1"
51 | },
52 | {
53 | 'name': 'shadow',
54 | 'dataType': 'boolean',
55 | 'value': False,
56 | 'required': False,
57 | 'displayName': "Mask cloud shadow",
58 | 'description': "Set cloud shadow pixels to 1"
59 | },
60 | {
61 | 'name': 'snow',
62 | 'dataType': 'boolean',
63 | 'value': False,
64 | 'required': False,
65 | 'displayName': "Mask snow",
66 | 'description': "Set snow pixels to 1"
67 | },
68 | {
69 | 'name': 'clear',
70 | 'dataType': 'boolean',
71 | 'value': False,
72 | 'required': False,
73 | 'displayName': "Mask clear",
74 | 'description': "Set clear pixels to 1"
75 | },
76 | {
77 | 'name': 'water',
78 | 'dataType': 'boolean',
79 | 'value': False,
80 | 'required': False,
81 | 'displayName': "Mask water",
82 | 'description': "Set water pixels to 1"
83 | },
84 | ]
85 |
86 | def getConfiguration(self, **scalars):
87 | return {
88 | 'compositeRasters': False,
89 | 'inheritProperties': 2 | 4 | 8, # inherit all from the raster but raster type
90 | 'invalidateProperties': 2 | 4 | 8, # reset stats, histogram, key properties
91 | 'inputMask': False
92 | }
93 |
94 | def updateRasterInfo(self, **kwargs):
95 | kwargs['output_info']['bandCount'] = 1
96 | kwargs['output_info']['histogram'] = () # reset histogram
97 | kwargs['output_info']['pixelType'] = 'u1'
98 | kwargs['output_info']['statistics'] = ({'minimum': 0, 'maximum': 1.0}, )
99 |
100 | fill = int(kwargs.get('fill'))
101 | diluted = int(kwargs.get('diluted'))
102 | cirrus = int(kwargs.get('cirrus'))
103 | cloud = int(kwargs.get('cloud'))
104 | shadow = int(kwargs.get('shadow'))
105 | snow = int(kwargs.get('snow'))
106 | clear = int(kwargs.get('clear'))
107 | water = int(kwargs.get('water'))
108 |
109 | self.bit_mask = (fill << self.bit_index['fill']) + (diluted << self.bit_index['diluted']) + (cirrus << self.bit_index['cirrus']) + (cloud << self.bit_index['cloud']) + (shadow << self.bit_index['shadow']) + (snow << self.bit_index['snow']) + (clear << self.bit_index['clear']) + (water << self.bit_index['water'])
110 |
111 | return kwargs
112 |
113 | def updatePixels(self, tlc, shape, props, **pixelBlocks):
114 | pix_blocks = pixelBlocks['r_pixels']
115 | pix_array = np.asarray(pix_blocks)
116 | z_dim, x_dim, y_dim = pix_array.shape
117 |
118 | out_mask = np.zeros(pix_array.shape)
119 |
120 | for num_x in range(x_dim):
121 | for num_y in range(y_dim):
122 | if pix_array[0, num_x, num_y] & self.bit_mask:
123 | out_mask[0, num_x, num_y] = 1 # set pixels that have a flag set to 1, otherwise 0
124 |
125 | pixelBlocks['output_pixels'] = out_mask.astype(props['pixelType'], copy=False)
126 |
127 | return pixelBlocks
128 |
--------------------------------------------------------------------------------
/functions/LandsatImageSynthesis.rft.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Landsat Image Synthesis
4 | Landsat Scene Generation Raster Function
5 |
6 | Landsat 5 Scene Synthesis
7 | Landsat 5 Scene Synthesis
8 | UNKNOWN
9 |
10 |
11 |
12 | rasters
13 | predict_month
14 | PythonModule
15 | ClassName
16 |
17 |
18 |
19 | rasters
20 |
21 |
22 |
23 | __IsRasterArray__
24 |
25 | false
26 |
27 |
28 | predict_month
29 |
30 | Mar
31 | false
32 |
33 | Landsat_Image_Synthesis.py
34 |
35 | ClassName
36 |
37 | Landsat_Image_Synthesis
38 | false
39 |
40 |
41 |
42 |
43 | __tans__(rasters,predict_month)
44 |
45 |
46 | 2
47 |
48 |
49 | Tag
50 | Name
51 |
52 |
53 |
--------------------------------------------------------------------------------
/functions/MaskRaster.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 |
3 | class MaskRaster():
4 |
5 | def __init__(self):
6 | self.name = "Mask Raster Function"
7 | self.description = "Applies a raster as the NoData mask of the input raster."
8 |
9 | def getParameterInfo(self):
10 | return [
11 | {
12 | 'name': 'r',
13 | 'dataType': 'raster',
14 | 'value': None,
15 | 'required': True,
16 | 'displayName': "Input Raster",
17 | 'description': "The primary input raster."
18 | },
19 | {
20 | 'name': 'm',
21 | 'dataType': 'raster',
22 | 'value': None,
23 | 'required': True,
24 | 'displayName': "Mask Raster",
25 | 'description': "The input mask raster."
26 | },
27 | ]
28 |
29 | def getConfiguration(self, **scalars):
30 | return {
31 | 'inputMask': True
32 | }
33 |
34 | def updatePixels(self, tlc, shape, props, **pixelBlocks):
35 | M = np.zeros(shape, 'u1')
36 | I = (pixelBlocks['m_pixels'] > 0) & (pixelBlocks['m_mask'] > 0)
37 | np.putmask(M, I, 1)
38 | pixelBlocks['output_mask'] = M
39 | pixelBlocks['output_pixels'] = pixelBlocks['r_pixels'].astype(props['pixelType'], copy=False)
40 | return pixelBlocks
41 |
--------------------------------------------------------------------------------
/functions/MaskRaster.rft.xml:
--------------------------------------------------------------------------------
1 |
2 | MaskRaster
3 | Apply a raster as the NoData mask of an input raster.
4 |
5 | Mask Raster Function
6 | Apply a raster as the NoData mask of an input raster.
7 | UNKNOWN
8 |
9 |
10 |
11 | PythonModule
12 | ClassName
13 | r
14 | m
15 |
16 |
17 | MaskRaster.py
18 | MaskRaster
19 |
20 | Raster1
21 |
22 |
23 | true
24 |
25 |
26 | Raster2
27 |
28 |
29 | true
30 |
31 |
32 |
33 |
34 | 2
35 |
36 |
37 | GroupName
38 | Tag
39 |
--------------------------------------------------------------------------------
/functions/PercentAboveThreshold.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | from datetime import timedelta
3 | import datetime
4 | #import sys
5 |
6 | #import os
7 | #import pickle
8 |
9 | #debug_logs_directory =
10 |
11 | class PercentAboveThreshold():
12 |
13 | def __init__(self):
14 | self.name = 'Percent Above or Below Threshold'
15 | self.description = 'Calculates the percentage of pixels that are above or below' \
16 | 'a threshold value. The threshold value is set in the raster function.' \
17 | 'The raster function can be applied to a time-enabled stack of rasters in ' \
18 | 'a mosaic dataset.'
19 |
20 | self.times = []
21 | self.start_year = None
22 | self.end_year = None
23 | self.threshold = 50
24 |
25 | def getParameterInfo(self):
26 | return [
27 | {
28 | 'name': 'rasters',
29 | 'dataType': 'rasters',
30 | 'value': None,
31 | 'required': True,
32 | 'displayName': 'Rasters',
33 | 'description': 'The collection of rasters to analyze.',
34 | },
35 | {
36 | 'name': 'start_date',
37 | 'dataType': 'string',
38 | 'value': '1/1/2019 12:30:00',
39 | 'required': True,
40 | 'displayName': 'Start Date',
41 | 'description': 'The beginning date of analysis (inclusive of entire year).',
42 | },
43 | {
44 | 'name': 'end_date',
45 | 'dataType': 'string',
46 | 'value': '12/31/2019 23:30:00',
47 | 'required': True,
48 | 'displayName': 'End Date',
49 | 'description': 'The final date of analysis (inclusive of entire year).',
50 | },
51 | {
52 | 'name': 'threshold',
53 | 'dataType': 'numeric',
54 | 'value': 45,
55 | 'required': True,
56 | 'displayName': 'Value Threshold',
57 | 'description': 'Value Threshold.',
58 | }
59 | ]
60 |
61 | def getConfiguration(self, **scalars):
62 | return {
63 | 'inheritProperties': 4 | 8, # inherit everything but the pixel type (1) and NoData (2)
64 | 'invalidateProperties': 2 | 4, # invalidate histogram and statistics because we are modifying pixel values
65 | 'inputMask': True, # need raster mask of all input rasters in .updatePixels().
66 | 'resampling': False, # process at native resolution
67 | 'keyMetadata': ['AcquisitionDate']
68 | }
69 |
70 | def updateRasterInfo(self, **kwargs):
71 | # outStats = {'minimum': -1, 'maximum': 1}
72 | # outStatsTuple = tuple(outStats for i in range(outBandCount))
73 |
74 | kwargs['output_info']['pixelType'] = 'f4' # output pixels are floating-point values
75 | kwargs['output_info']['histogram'] = () # no statistics/histogram for output raster specified
76 | kwargs['output_info']['statistics'] = () # outStatsTuple
77 | #kwargs['output_info'][
78 | # 'bandCount'] = outBandCount # number of output bands. 7 time bands, 3 TC bands, creates 21 bands
79 |
80 | self.times = kwargs['rasters_keyMetadata']
81 | self.start_date = kwargs['start_date']
82 | self.end_date = kwargs['end_date']
83 | self.threshold = int(kwargs['threshold'])
84 |
85 | return kwargs
86 |
87 | def updateKeyMetadata(self, names, bandIndex, **keyMetadata):
88 | return keyMetadata
89 |
90 | def updatePixels(self, tlc, shape, props, **pixelBlocks):
91 |
92 | #fname = '{:%Y_%b_%d_%H_%M_%S}_t.txt'.format(datetime.datetime.now())
93 | #filename = os.path.join(debug_logs_directory, fname)
94 |
95 | #file = open(filename,"w")
96 | #file.write("File Open.\n")
97 |
98 | pix_time = [j['acquisitiondate'] for j in self.times]
99 |
100 |
101 | #pickle_filename = os.path.join(debug_logs_directory, fname)
102 | #pickle.dump(pix_time, open(pickle_filename[:-4]+'pix_time.p',"wb"))
103 |
104 | #file.write(str(len(pix_time))+ "\n")
105 |
106 | pix_blocks = pixelBlocks['rasters_pixels']
107 | pix_array = np.asarray(pix_blocks)
108 |
109 | #pickle_filename = os.path.join(debug_logs_directory, fname)
110 | #pickle.dump(pix_array, open(pickle_filename[:-4]+'pix_blocks.p',"wb"))
111 |
112 | pix_array_dim = pix_array.shape
113 | num_squares_x = pix_array_dim[2]
114 | num_squares_y = pix_array_dim[3]
115 |
116 | #file.write("Filtering Based on Time\n")
117 |
118 | # This worked before I added time Filtering:
119 | #pix_as_array = np.reshape(pix_array, -1)
120 | #total_count = np.size(pix_as_array)
121 | #vals_above_thresh_count = np.size(np.where(pix_as_array <= self.threshold))
122 | #outBlock = np.ones((num_squares_x, num_squares_y)) * (vals_above_thresh_count / total_count) * 100
123 |
124 | t_array = []
125 | ind_array = []
126 | start_date = self.start_date #"1/1/2019 12:30:00"
127 | end_date = self.end_date #"7/7/2019 12:30:00"
128 | start_datetime = datetime.datetime.strptime(start_date, '%m/%d/%Y %H:%M:%S') # %p')
129 | end_datetime = datetime.datetime.strptime(end_date, '%m/%d/%Y %H:%M:%S') # %p')
130 | for ind, time in enumerate(pix_time):
131 | temp_t = datetime.datetime(1900, 1, 1) + timedelta(time - 2)
132 | if temp_t >= start_datetime and temp_t <= end_datetime:
133 | t_array.append(temp_t)
134 | ind_array.append(ind)
135 |
136 | #time_within = [pix_time[x] for x in ind_array]
137 | pix_array_within = pix_array[ind_array, :, :, :]
138 |
139 | #threshold = 50
140 | pix_as_array = np.reshape(pix_array_within, -1)
141 | total_count = np.size(pix_as_array)
142 | vals_above_thresh_count = np.size(np.where(pix_as_array <= self.threshold)) #< below, > above
143 | outBlock = np.ones((num_squares_x, num_squares_y)) * (vals_above_thresh_count / total_count) * 100
144 |
145 | #file.write("DONE\n")
146 | #file.close()
147 | pixelBlocks['output_pixels'] = outBlock.astype(props['pixelType'], copy=False)
148 | #masks = np.array(pixelBlocks['rasters_mask'], copy=False)
149 | #pixelBlocks['output_mask'] = np.all(masks, axis=0).astype('u1', copy=False)
150 | return pixelBlocks
151 |
--------------------------------------------------------------------------------
/functions/PercentAboveThreshold.rft.xml:
--------------------------------------------------------------------------------
1 | PercentAboveThresholdCalculates the percentage of pixels above a threshold value.Percent Above or Below ThresholdCalculates the percentage of pixels that are above or belowa threshold value. The threshold value is set in the raster function.The raster function can be applied to a time-enabled stack of rasters in a mosaic dataset.UNKNOWNrastersstart_dateend_datethresholdPythonModuleClassNamerasters__IsRasterArray__falsestart_date1/1/2019 12:30:00falseend_date12/31/2019 23:30:00falsethreshold50falseC:\PROJECTS\gbrunner-raster-functions\functions\PercentAboveThreshold.pyClassNamePercentAboveThresholdfalse__tans__(rasters,start_date,end_date,threshold)Calculates the percentage of pixels above a threshold value.2VariableOBJECTID
--------------------------------------------------------------------------------
/functions/RandomForestClassifier.py:
--------------------------------------------------------------------------------
1 | import pandas as pd
2 | from sklearn.ensemble import RandomForestClassifier
3 | import numpy as np
4 |
5 | '''
6 | A random forest classifier.
7 |
8 | A random forest is a meta estimator that fits a number of decision tree classifiers on
9 | various sub-samples of the dataset and uses averaging to improve the
10 | predictive accuracy and control over-fitting.
11 | The sub-sample size is controlled with the max_samples parameter if bootstrap=True (default),
12 | otherwise the whole dataset is used to build each tree.
13 |
14 | https://scikit-learn.org/stable/modules/generated/sklearn.ensemble.RandomForestClassifier.html?
15 | highlight=random%20forest#sklearn.ensemble.RandomForestClassifier
16 | '''
17 |
18 | # Note: Can not name the class RandomForestClassifier because it will conflict
19 | # with the scikit-learn class that is imported.
20 | class RandomForest():
21 | def __init__(self):
22 | self.name = 'Random Forest Classifier'
23 | self.description = 'Random Forest Classifier implemented as a Python Raster Function'
24 |
25 | # inputs as string, but eventually will be numpy arrays
26 | self.df = None
27 | self.datafile = None
28 | self.threshold = 0.5
29 |
30 |
31 | def getParameterInfo(self):
32 | return [
33 | {
34 | 'name': 'rasters',
35 | 'dataType': 'rasters',
36 | 'value': None,
37 | 'required': True,
38 | 'displayName': 'Input Rasters',
39 | 'description': 'Must be in the same order as the columns in the CSV file'
40 | },
41 | {
42 | 'name': 'training_data_from_file',
43 | 'dataType': 'string',
44 | 'value': 'C:\\PROJECTS\\ML\\training_data.csv',
45 | 'required': True,
46 | 'displayName': 'Training data CSV filepath',
47 | 'description': 'Full filepath directory to training data CSV. '
48 | 'Internally this will load from disk and be converted to a pandas dataframe.'
49 | }
50 | ]
51 |
52 | def getConfiguration(self, **scalars):
53 | return {
54 | 'inheritProperties': 1 | 2 | 4 | 8, # inherit all from the raster
55 | 'invalidateProperties': 2 | 4 | 8 # reset stats, histogram, key properties
56 | }
57 |
58 | def updateRasterInfo(self, **kwargs):
59 |
60 | # convert filepath string input param to numpy array
61 | self.datafile = str(kwargs['training_data_from_file'])
62 | #self.threshold = float(kwargs['threshold'])
63 |
64 | kwargs['output_info']['pixelType'] = 'f4'
65 | kwargs['output_info']['histogram'] = ()
66 | kwargs['output_info']['statistics'] = ()
67 | kwargs['output_info']['bandCount'] = 3
68 |
69 | return kwargs
70 |
71 | def updatePixels(self, tlc, shape, props, **pixelBlocks):
72 |
73 | self.df = pd.read_csv(self.datafile)
74 |
75 | try:
76 | fields_to_drop = ['OBJECTID', 'LOCATION_X', 'LOCATION_Y']
77 | self.df.drop(fields_to_drop, axis=1, inplace=True)
78 | except:
79 | pass
80 |
81 | y_val = 'VarToPredict' # The value that you probably want to predict
82 | x_train = self.df.loc[:, self.df.columns != y_val]
83 | y_train = self.df[y_val]
84 | x_train.fillna(0, inplace=True)
85 | y_train.fillna(0, inplace=True)
86 |
87 | # Initialize RandomForestClassifier
88 | # Recommend trying different values for:
89 | # - n_estimators
90 | # - max_features
91 | # - random_state
92 | regr1 = RandomForestClassifier(n_estimators=20, random_state=0) # max_features=13, random_state=1)
93 | regr1.fit(x_train, y_train)
94 |
95 | pix_blocks = pixelBlocks['rasters_pixels']
96 | pix_array = np.asarray(pix_blocks)
97 | pix_array = np.squeeze(pix_array)
98 |
99 | pixels_reshaped = pix_array.reshape(pix_array.shape[0], -1).transpose()
100 |
101 | # Run RandomForestRegressor
102 | pred = regr1.predict(pixels_reshaped)
103 | pred_proba = regr1.predict_proba(pixels_reshaped)
104 |
105 | res = np.hstack([np.expand_dims(pred, 1), pred_proba])
106 |
107 | #res = pred.reshape((pix_array.shape[1], pix_array.shape[2]))
108 | res_reshape = np.reshape(
109 | res.transpose(),
110 | (3, pix_array.shape[1], pix_array.shape[2])
111 | )
112 |
113 | res_reshape[res_reshape <= self.threshold] = 0
114 |
115 | pixelBlocks['output_pixels'] = res_reshape.astype(
116 | props['pixelType'],
117 | copy=True
118 | )
119 |
120 | return pixelBlocks
121 |
122 | def updateKeyMetadata(self, names, bandIndex, **keyMetadata):
123 | return keyMetadata
124 |
--------------------------------------------------------------------------------
/functions/RankFilter.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | from skimage.transform import resize
3 | from skimage.util import view_as_blocks
4 | from skimage.filters import rank
5 | from skimage.morphology import square
6 | from utils import Trace
7 |
8 |
9 | class RankFilter():
10 |
11 | def __init__(self):
12 | self.name = "Rank Filter Function"
13 | self.description = ("Apply a non-linear filter on a local neighborhood of inputs pixels in a sliding window.")
14 | self.func = rank.mean
15 | self.window = None
16 | self.trace = Trace()
17 | self.padding = 0
18 |
19 | def getParameterInfo(self):
20 | return [
21 | {
22 | 'name': 'raster',
23 | 'dataType': 'raster',
24 | 'value': None,
25 | 'required': True,
26 | 'displayName': "Input Raster",
27 | 'description': ("The primary input raster on which the filter is applied.")
28 | },
29 | {
30 | 'name': 'measure',
31 | 'dataType': 'string',
32 | 'value': 'Mean',
33 | 'required': False,
34 | 'displayName': "Measure",
35 | 'domain': ('Minimum', 'Maximum', 'Mean', 'Bilateral Mean', 'Median',
36 | 'Sum', 'Entropy', 'Threshold', 'Autolevel'),
37 | 'description': ("The measure represented by an ouput pixel "
38 | "computed over a sliding window of input pixels.")
39 | },
40 | {
41 | 'name': 'size',
42 | 'dataType': 'numeric',
43 | 'value': 5,
44 | 'required': False,
45 | 'displayName': "Window Size",
46 | 'description': ("The width of the sliding window or kernel (in pixels).")
47 | },
48 | {
49 | 'name': 'res',
50 | 'dataType': 'string',
51 | 'value': 'Request',
52 | 'required': False,
53 | 'displayName': "Resolution",
54 | 'domain': ('Request', 'Raster'),
55 | 'description': ("The resolution at which the filter is applied. "
56 | "Choose between processing input pixels at resampled display/request resolution "
57 | "or in the original/raster resolution.")
58 | },
59 | ]
60 |
61 | def getConfiguration(self, **scalars):
62 | r = scalars.get('res', None)
63 | s = scalars.get('size', None)
64 | s = 3 if s is None else s
65 | self.padding = int(s / 2)
66 |
67 | return {
68 | 'inheritProperties': 4 | 8, # inherit everything but the pixel type (1) and NoData (2)
69 | 'invalidateProperties': 2 | 4 | 8, # invalidate histogram, statistics, and key metadata
70 | 'inputMask': True,
71 | 'padding': self.padding,
72 | 'resampling': not(r is not None and str(r).lower() == 'raster')
73 | }
74 |
75 | def updateRasterInfo(self, **kwargs):
76 | kwargs['output_info']['statistics'] = ()
77 | kwargs['output_info']['histogram'] = ()
78 |
79 | self.window = square(int(kwargs.get('size', 3)))
80 | m = kwargs.get('measure', 'Mean').lower()
81 | if m == 'minimum':
82 | self.func = rank.minimum
83 | elif m == 'maximum':
84 | self.func = rank.maximum
85 | elif m == 'mean':
86 | self.func = rank.mean
87 | elif m == 'bilateral mean':
88 | self.func = rank.mean_bilateral
89 | elif m == 'median':
90 | self.func = rank.median
91 | elif m == 'sum':
92 | self.func = rank.sum
93 | elif m == 'entropy':
94 | self.func = rank.entropy
95 | elif m == 'threshold':
96 | self.func = rank.threshold
97 | elif m == 'autolevel':
98 | self.func = rank.autolevel
99 | return kwargs
100 |
101 | def updatePixels(self, tlc, shape, props, **pixelBlocks):
102 | p = pixelBlocks['raster_pixels']
103 | m = pixelBlocks['raster_mask']
104 |
105 | q = np.empty(p.shape)
106 | for b in range(p.shape[0]):
107 | q[b] = self.func(p[b], selem=self.window, mask=m[b])
108 |
109 | d = self.padding
110 | pixelBlocks['output_pixels'] = q[0][d:-d, d:-d].astype(props['pixelType'], copy=False)
111 | return pixelBlocks
112 |
113 | def updateKeyMetadata(self, names, bandIndex, **keyMetadata):
114 | if bandIndex == -1:
115 | keyMetadata['datatype'] = 'Processed'
116 | return keyMetadata
117 |
118 |
119 | # ----- ## ----- ## ----- ## ----- ## ----- ## ----- ## ----- ## ----- ##
120 |
121 | """
122 | References:
123 |
124 | [1] Sicuranza, G., 2000. Nonlinear image processing. Academic Press.
125 |
126 | [2] Rank Filters for Image Processing.
127 | http://www.numerical-tours.com/matlab/denoisingadv_7_rankfilters/
128 |
129 | [3] Scikit-image: Image processing in Python (Rank filters).
130 | http://scikit-image.org/docs/dev/auto_examples/applications/plot_rank_filters.html
131 | """
132 |
--------------------------------------------------------------------------------
/functions/RankFilter.rft.xml:
--------------------------------------------------------------------------------
1 |
2 | RankFilter
3 | A raster function template.
4 |
5 | Rank Filter Function
6 | Apply a non-linear filter on a local neighborhood of inputs pixels in a sliding window.
7 | UNKNOWN
8 |
9 |
10 |
11 | PythonModule
12 | ClassName
13 | raster
14 | measure
15 | size
16 | res
17 |
18 |
19 | RankFilter.py
20 | RankFilter
21 |
22 | Raster
23 |
24 |
25 | true
26 |
27 |
28 | measure
29 |
30 | Mean
31 | false
32 |
33 |
34 | size
35 |
36 | 5
37 | false
38 |
39 |
40 | res
41 |
42 | Request
43 | false
44 |
45 |
46 |
47 |
48 | 0
49 |
50 |
51 |
52 |
53 |
54 |
--------------------------------------------------------------------------------
/functions/ReplaceNulls.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 |
3 | class ReplaceNulls():
4 | def __init__(self):
5 | self.name = 'Replace Nulls'
6 | self.description = 'Replace NULL values in a raster with a user defined value.'
7 |
8 |
9 |
10 | def getParameterInfo(self):
11 | return [
12 | {
13 | 'name': 'raster',
14 | 'dataType': 'raster',
15 | 'value':'Multiband Raster',
16 | 'required': True,
17 | 'displayName': 'Input Raster',
18 | 'description': 'Input Raster'
19 | },
20 | {
21 | 'name': 'fill_val',
22 | 'dataType': 'numeric',
23 | 'value': 1,
24 | 'required': True,
25 | 'displayName': 'Replace Value',
26 | 'description': 'Value to replace with'
27 | }
28 | ]
29 |
30 | def getConfiguration(self, **scalars):
31 | return {
32 | 'inheritProperties': 1 | 2 | 4 | 8, # inherit everything but the pixel type (1) and NoData (2)
33 | #'invalidateProperties': 1 | 2 | 4 | 8, # invalidate histogram and statistics because we are modifying pixel values
34 | 'resampling': False
35 | }
36 |
37 | def updateRasterInfo(self, **kwargs):
38 |
39 | self.fill_val = float(kwargs['fill_val'])
40 | # repeat stats for all output raster bands
41 | #kwargs['output_info']['statistics'] = tuple(outStats for i in range(self.out_band_count))
42 | kwargs['output_info']['pixelType'] = 'f4'
43 | kwargs['output_info']['statistics'] = ()
44 |
45 | return kwargs
46 |
47 | def updatePixels(self, tlc, shape, props, **pixelBlocks):
48 |
49 | pix_array = np.asarray(pixelBlocks['raster_pixels'])
50 | np.place(pix_array, pix_array==0, [self.fill_val])
51 |
52 | mask = np.ones(pix_array.shape)
53 | pixelBlocks['output_mask'] = mask.astype('u1', copy = False)
54 | pixelBlocks['output_pixels'] = pix_array.astype(props['pixelType'], copy=True)
55 |
56 |
57 | return pixelBlocks
58 |
59 |
60 |
--------------------------------------------------------------------------------
/functions/ReplaceNulls.rft.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | ReplaceNulls
4 | Replace Nulls
5 |
6 | Replace Nulls
7 | Replace NULL values in a raster with a user defined value.
8 | UNKNOWN
9 |
10 |
11 |
12 | raster
13 | fill_val
14 | PythonModule
15 | ClassName
16 |
17 |
18 |
19 | Raster
20 |
21 | Multiband Raster
22 | true
23 |
24 |
25 | fill_val
26 |
27 | 1
28 | false
29 |
30 | C:\PROJECTS\gbrunner-raster-functions\functions\ReplaceNulls.py
31 |
32 | ClassName
33 |
34 | ReplaceNulls
35 | false
36 |
37 |
38 |
39 |
40 | __tans__(raster,fill_val)
41 |
42 | Replace Nulls
43 | 0
44 |
45 |
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/functions/SeasonalARIMA.rft.xml:
--------------------------------------------------------------------------------
1 | Seasonal ARIMASeasonal ARIMASeasonal ARIMAThis function predicts the change in the observed pixel value given a time series of values.UNKNOWNrastersdata_start_yeartrain_start_yeartrain_end_yearpredict_yearpredict_monthseasonal_orderPythonModuleClassNamerasters__IsRasterArray__falsedata_start_year1980falsetrain_start_year1980falsetrain_end_year2010falsepredict_year2050falsepredict_monthJunfalseseasonal_order0, 1, 1, 12falseC:\PROJECTS\gbrunner-raster-functions\functions\SeasonalARIMA.pyClassNameSeasonalARIMAfalse__tans__(rasters,data_start_year,train_start_year,train_end_year,predict_year,predict_month,seasonal_order)2VariableOBJECTID
--------------------------------------------------------------------------------
/functions/SelectByPixelSize.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | import utils
3 |
4 | class SelectByPixelSize():
5 |
6 | def __init__(self):
7 | self.name = "Select by Pixel Size"
8 | self.description = "This function returns pixels associated with one of two input rasters based on the request resolution."
9 | self.threshold = 0.0
10 | self.inBands1, self.inBands2, self.outBands = 1, 1, 1
11 | self.trace = utils.Trace()
12 |
13 | def getParameterInfo(self):
14 | return [
15 | {
16 | 'name': 'r1',
17 | 'dataType': 'raster',
18 | 'value': None,
19 | 'required': True,
20 | 'displayName': "Raster 1",
21 | 'description': ("The raster that's returned when request cell size is lower than "
22 | "the 'Cell Size Threshold'. A lower cell size value implies finer resolution.")
23 | },
24 | {
25 | 'name': 'r2',
26 | 'dataType': 'raster',
27 | 'value': None,
28 | 'required': True,
29 | 'displayName': "Raster 2",
30 | 'description': ("The raster that's returned when request cell size is higher than or equal to "
31 | "the 'Cell Size Threshold'. A higher cell size value implies coarser resolution.")
32 | },
33 | {
34 | 'name': 'threshold',
35 | 'dataType': 'numeric',
36 | 'value': 0.0,
37 | 'required': True,
38 | 'displayName': "Cell Size Threshold",
39 | 'description': ("The cell size threshold that controls which of the two input "
40 | "rasters contributes pixels to the output.")
41 | },
42 | ]
43 |
44 | def getConfiguration(self, **scalars):
45 | return {
46 | 'inputMask': True,
47 | 'resampling': True
48 | }
49 |
50 | def updateRasterInfo(self, **kwargs):
51 | self.threshold = float(kwargs.get('threshold', 0.0))
52 | if self.threshold <= 0.0:
53 | self.threshold = np.mean((np.mean(kwargs['r1_info']['cellSize']), np.mean(kwargs['r2_info']['cellSize'])))
54 |
55 | self.inBands1 = kwargs['r1_info']['bandCount']
56 | self.inBands2 = kwargs['r2_info']['bandCount']
57 | kwargs['output_info']['bandCount'] = min(self.inBands1, self.inBands2)
58 | kwargs['output_info']['statistics'] = ()
59 | kwargs['output_info']['histogram'] = ()
60 |
61 | self.trace.log("Trace|Threshold cell-size|{0}\n".format(self.threshold))
62 | self.trace.log("Trace|output_info|{0}\n".format(kwargs['output_info']))
63 | return kwargs
64 |
65 | def selectRasters(self, tlc, shape, props):
66 | cellSize = props['cellSize']
67 | v = 0.5 * (cellSize[0] + cellSize[1])
68 | if v < self.threshold:
69 | return ('r1',)
70 | else: return ('r2',)
71 |
72 | def updatePixels(self, tlc, shape, props, **pixelBlocks):
73 | cellSize = props['cellSize']
74 | v = 0.5 * (cellSize[0] + cellSize[1])
75 | self.trace.log("Trace|Request cell-size|{0}\n".format(v))
76 |
77 | if v < self.threshold:
78 | sPixels = 'r1_pixels'
79 | sMask = 'r1_mask'
80 | nBands = self.inBands1
81 | else:
82 | sPixels = 'r2_pixels'
83 | sMask = 'r2_mask'
84 | nBands = self.inBands2
85 |
86 | if self.outBands == nBands:
87 | p = pixelBlocks[sPixels]
88 | m = pixelBlocks[sMask]
89 | else:
90 | p = pixelBlocks[sPixels][0:self.outBands, :, :]
91 | m = pixelBlocks[sMask][0:self.outBands, :, :]
92 |
93 | pixelBlocks['output_pixels'] = p.astype(props['pixelType'])
94 | pixelBlocks['output_mask'] = m.astype('u1')
95 | return pixelBlocks
96 |
--------------------------------------------------------------------------------
/functions/SelectByPixelSize.rft.xml:
--------------------------------------------------------------------------------
1 |
2 | SelectByPixelSize
3 | A raster function template.
4 |
5 | Select by Pixel Size
6 |
7 | UNKNOWN
8 |
9 |
10 |
11 | PythonModule
12 | ClassName
13 | threshold
14 | r1
15 | r2
16 |
17 |
18 | SelectByPixelSize.py
19 | SelectByPixelSize
20 | 0
21 |
22 | Raster1
23 |
24 |
25 | true
26 |
27 |
28 | Raster2
29 |
30 |
31 | true
32 |
33 |
34 |
35 |
36 | 2
37 |
38 |
39 | GroupName
40 | Tag
41 |
42 |
--------------------------------------------------------------------------------
/functions/StepwiseLocalRadiometricAdjustment.rft.xml:
--------------------------------------------------------------------------------
1 | Stepwise Local Radiometric AdjustmentPython raster function for blending areas using Stepwise Local Radiometric Adjustment algorithm.The stepwise local radiometric adjustment is undertaken to fill contaminated areas and is conducted on each mask region of the target imageStepwise Local Radiometric AdjustmentPython raster function for blending areas using Stepwise Local Radiometric Adjustment algorithm.The stepwise local radiometric adjustment is undertaken to fill contaminated areas and is conducted on each mask region of the target imageUNKNOWNinput_rasterinput_replacement_rasterinput_masksize_of_windowPythonModuleClassNameRastertrueinput_replacement_rastertrueinput_masktruesize_of_window80falseC:\Esri_project\cloud_masking_algo\python_raster_fucntion\StepwiseLocalRadiometricAdjustment.pyClassNameStepwiseLocalRadiometricAdjustmentfalse0MatchVariableMatchVariable1falseUnionDimensionUnionDimension0false
--------------------------------------------------------------------------------
/functions/VF.rft.xml:
--------------------------------------------------------------------------------
1 |
2 | VF
3 | A raster function template.
4 |
5 | Vector Field Function
6 | Combines magnitude(u) and direction(v) rasters in scientific datasets to produce a two band output.
7 | UNKNOWN
8 |
9 |
10 |
11 | Raster1
12 | Raster2
13 | InputDataType
14 | OutputDataType
15 | AngleReferenceSystem
16 | __Properties
17 |
18 |
19 |
20 | Raster1
21 |
22 |
23 | true
24 |
25 |
26 | Raster2
27 |
28 |
29 | true
30 |
31 |
32 | InputDataType
33 |
34 | Vector-UV
35 | false
36 |
37 |
38 | OutputDataType
39 |
40 | Vector-MagDir
41 | false
42 |
43 |
44 | AngleReferenceSystem
45 |
46 | 1
47 | false
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 | 0
56 |
57 |
58 | GroupName
59 | Tag
60 |
61 |
--------------------------------------------------------------------------------
/functions/VineyardAnalysis.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 |
3 |
4 | class VineyardAnalysis():
5 |
6 | def __init__(self):
7 | self.name = "Vineyard Suitability Analysis Function"
8 | self.description = "This function computes vineyard suitability given elevation, slope, aspect, and soil-type rasters."
9 |
10 | def getParameterInfo(self):
11 | return [
12 | {
13 | 'name': 'elevation',
14 | 'dataType': 'raster',
15 | 'value': None,
16 | 'required': True,
17 | 'displayName': "Elevation Raster",
18 | 'description': "The primary single-band raster where pixel values represent elevation in meters."
19 | },
20 | {
21 | 'name': 'slope',
22 | 'dataType': 'raster',
23 | 'value': None,
24 | 'required': True,
25 | 'displayName': "Slope Raster",
26 | 'description': "A single-band raster where pixel values represent slope."
27 | },
28 | {
29 | 'name': 'aspect',
30 | 'dataType': 'raster',
31 | 'value': None,
32 | 'required': True,
33 | 'displayName': "Aspect Raster",
34 | 'description': "A single-band raster where pixel values represent aspect."
35 | },
36 | {
37 | 'name': 'soiltype',
38 | 'dataType': 'raster',
39 | 'value': None,
40 | 'required': False,
41 | 'displayName': "Soil Type Raster",
42 | 'description': "A single-band thematic raster where pixel values represent soil type."
43 | },
44 | ]
45 |
46 | def getConfiguration(self, **scalars):
47 | return {
48 | 'inheritProperties': 2 | 4 | 8, # inherit all but the pixel type from the input raster
49 | 'invalidateProperties': 2 | 4 | 8, # reset any statistics and histogram that might be held by
50 | # the parent dataset (because this function modifies pixel values).
51 | 'inputMask': True # We need the input raster mask in .updatePixels().
52 | }
53 |
54 | def updateRasterInfo(self, **kwargs):
55 | kwargs['output_info']['bandCount'] = 1
56 | kwargs['output_info']['pixelType'] = 'u1'
57 | kwargs['output_info']['statistics'] = ({'minimum': 0, 'maximum': 3}, )
58 | kwargs['output_info']['noData'] = np.array([0], 'u1')
59 | return kwargs
60 |
61 | def updatePixels(self, tlc, shape, props, **pixelBlocks):
62 | elev = np.array(pixelBlocks['elevation_pixels'], dtype='f4', copy=False)
63 | slope = np.array(pixelBlocks['slope_pixels'], dtype='f4', copy=False)
64 | aspect = np.array(pixelBlocks['aspect_pixels'], dtype='f4', copy=False)
65 | # soil = np.array(pixelBlocks['soiltype_pixels'], 'i8')
66 |
67 | E = (elev > 30).astype('u1', copy=False) & (elev < 400).astype('u1', copy=False)
68 | S = (slope > 5).astype('u1', copy=False) & (slope < 60).astype('u1', copy=False)
69 | A = (aspect > 0).astype('u1', copy=False) & (aspect < 200).astype('u1', copy=False)
70 | pixelBlocks['output_pixels'] = (E + S + A).astype(props['pixelType'], copy=False)
71 | return pixelBlocks
72 |
73 | def updateKeyMetadata(self, names, bandIndex, **keyMetadata):
74 | if bandIndex == -1:
75 | keyMetadata['datatype'] = 'Scientific'
76 | keyMetadata['variable'] = 'VineyardSuitability'
77 | elif bandIndex == 0:
78 | keyMetadata['wavelengthmin'] = None # reset inapplicable band-specific key metadata
79 | keyMetadata['wavelengthmax'] = None
80 | keyMetadata['bandname'] = 'VineyardSuitability'
81 | return keyMetadata
82 |
--------------------------------------------------------------------------------
/functions/VineyardAnalysis.rft.xml:
--------------------------------------------------------------------------------
1 |
2 | VineyardAnalysis
3 | A raster function template.
4 |
5 | Python Adapter Function
6 | Adapter function for raster functions written in python.
7 | UNKNOWN
8 |
9 |
10 |
11 | PythonModule
12 | ClassName
13 | elevation
14 | slope
15 | aspect
16 |
17 |
18 | VineyardAnalysis.py
19 | VineyardAnalysis
20 |
21 | Raster
22 |
23 |
24 | true
25 |
26 |
27 | Raster Function Template
28 | A raster function template.
29 |
30 | Slope Function
31 | Calculates the rate of change of elevation for each DEM cell.
32 | UNKNOWN
33 |
34 |
35 |
36 | DEM
37 | ZFactor
38 | PSPower
39 | PSZFactor
40 | SlopeType
41 | RemoveEdgeEffect
42 |
43 |
44 |
45 | Raster
46 |
47 |
48 | true
49 |
50 |
51 | ZFactor_201499_0627_233
52 |
53 | 1
54 | false
55 |
56 |
57 | PSPower_201499_0627_233
58 |
59 | 0.66400000000000003
60 | false
61 |
62 |
63 | PSZFactor_201499_0627_233
64 |
65 | 0.024
66 | false
67 |
68 |
69 | SlopeType_201499_0627_233
70 |
71 | 1
72 | false
73 |
74 |
75 | RemoveEdgeEffect_201499_0627_233
76 |
77 | false
78 | false
79 |
80 |
81 |
82 |
83 | 0
84 |
85 |
86 |
87 |
88 |
89 |
90 | Raster Function Template
91 | A raster function template.
92 |
93 | Aspect Function
94 | Identifies the downslope direction of the maximum rate of change in value from each cell to its neighbors.
95 | UNKNOWN
96 |
97 |
98 | Raster
99 |
100 |
101 | true
102 |
103 |
104 | 0
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 | 0
114 |
115 |
116 |
117 |
118 |
--------------------------------------------------------------------------------
/functions/deprecated/Aggregate.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 |
3 |
4 | class Aggregate():
5 |
6 | def __init__(self):
7 | self.name = "Aggregate Rasters Function"
8 | self.description = "This function aggregates pixel values over a collection of overlapping single-band rasters."
9 | self.operator = np.sum
10 |
11 | def getParameterInfo(self):
12 | return [
13 | {
14 | 'name': 'rasters',
15 | 'dataType': 'rasters',
16 | 'value': None,
17 | 'required': True,
18 | 'displayName': "Rasters",
19 | 'description': "The collection of overlapping rasters to aggregate.",
20 | },
21 | {
22 | 'name': 'method',
23 | 'dataType': 'string',
24 | 'value': 'Sum',
25 | 'required': False,
26 | 'displayName': "Method",
27 | 'domain': ('Sum', 'Average', 'Median', 'Standard Deviation', 'Minimum', 'Maximum'),
28 | 'description': "The method indicating how overlapping pixels of the input rasters are aggregated.",
29 | },
30 | ]
31 |
32 | def getConfiguration(self, **scalars):
33 | m = scalars.get('method', 'Sum').lower()
34 | if m == 'average': self.operator = np.mean
35 | elif m == 'median': self.operator = np.median
36 | elif m == 'minimum': self.operator = np.min
37 | elif m == 'maximum': self.operator = np.max
38 | elif m == 'standard deviation': self.operator = np.std
39 | else: self.operator = np.sum
40 |
41 | return {
42 | 'inheritProperties': 4 | 8, # inherit everything but the pixel type (1) and NoData (2)
43 | 'invalidateProperties': 2 | 4, # invalidate histogram and statistics because we are modifying pixel values
44 | 'inputMask': True, # need raster mask of all input rasters in .updatePixels().
45 | 'resampling': False # process at native resolution
46 |
47 | }
48 |
49 | def updateRasterInfo(self, **kwargs):
50 | kwargs['output_info']['pixelType'] = 'f4' # output pixels are floating-point values
51 | kwargs['output_info']['noData'] = None # we'll set the mask updatePixels()
52 | kwargs['output_info']['histogram'] = () # no statistics/histogram for output raster specified
53 | kwargs['output_info']['statistics'] = ()
54 | return kwargs
55 |
56 | def updatePixels(self, tlc, shape, props, **pixelBlocks):
57 | # pixelBlocks['rasters_pixels']: tuple of 3-d array containing pixel blocks from each input raster
58 | # apply the selected operator over each array in the tuple
59 | outBlock = self.operator(pixelBlocks['rasters_pixels'], axis=0)
60 | pixelBlocks['output_pixels'] = outBlock.astype(props['pixelType'], copy=False)
61 | masks = np.array(pixelBlocks['rasters_mask'], copy=False)
62 | pixelBlocks['output_mask'] = np.all(masks, axis=0).astype('u1', copy=False)
63 | return pixelBlocks
64 |
--------------------------------------------------------------------------------
/functions/deprecated/Aggregate.rft.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Aggregate
4 | A raster function template.
5 |
6 | Aggregate Rasters Function
7 | This function aggregates pixel values over a collection of overlapping single-band rasters.
8 | F32
9 |
10 |
11 |
12 | PythonModule
13 | Rasters
14 | ClassName
15 | method
16 |
17 |
18 | Aggregate.py
19 |
20 | Raster[]
21 | The set of overlapping rasters to aggregate.
22 |
23 | false
24 |
25 | Aggregate
26 |
27 | method
28 |
29 | Average
30 | false
31 |
32 |
33 |
34 |
35 | 2
36 |
37 |
38 | GroupName
39 | Tag
40 |
41 |
--------------------------------------------------------------------------------
/functions/deprecated/Arithmetic.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 |
3 |
4 | class Arithmetic():
5 | def __init__(self):
6 | self.name = "Arithmetic Function"
7 | self.description = "Performs simple arithmetic operations on two rasters."
8 | self.op = None
9 |
10 | def getParameterInfo(self):
11 | return [
12 | {
13 | 'name': 'r1',
14 | 'dataType': 'raster',
15 | 'value': None,
16 | 'required': True,
17 | 'displayName': "Raster A",
18 | 'description': ""
19 | },
20 | {
21 | 'name': 'r2',
22 | 'dataType': 'raster',
23 | 'value': None,
24 | 'required': True,
25 | 'displayName': "Raster B",
26 | 'description': ""
27 | },
28 | {
29 | 'name': 'op',
30 | 'dataType': 'string',
31 | 'value': 'Add',
32 | 'required': False,
33 | 'domain': ('Add', 'Subtract', 'Multiply', 'Divide'),
34 | 'displayName': "Operation",
35 | 'description': ""
36 | },
37 | ]
38 |
39 | def getConfiguration(self, **scalars):
40 | return {
41 | 'inheritProperties': 2 | 4 | 8,
42 | 'invalidateProperties': 2 | 4 | 8,
43 | 'resampling': True # process at request resolution
44 | }
45 |
46 | def updateRasterInfo(self, **kwargs):
47 | m = kwargs.get('op', 'Add').lower()
48 |
49 | if m == 'add': self.op = np.add
50 | elif m == 'subtract': self.op = np.subtract
51 | elif m == 'multiply': self.op = np.multiply
52 | elif m == 'divide': self.op = np.divide
53 |
54 | kwargs['output_info']['statistics'] = ()
55 | kwargs['output_info']['histogram'] = ()
56 | return kwargs
57 |
58 | def updatePixels(self, tlc, shape, props, **pixelBlocks):
59 | r1 = np.array(pixelBlocks['r1_pixels'], dtype='f4', copy=False)
60 | r2 = np.array(pixelBlocks['r2_pixels'], dtype='f4', copy=False)
61 |
62 | np.seterr(divide='ignore')
63 | pixelBlocks['output_pixels'] = self.op(r1, r2).astype(props['pixelType'], copy=False)
64 | return pixelBlocks
65 |
66 | def updateKeyMetadata(self, names, bandIndex, **keyMetadata):
67 | if bandIndex == -1:
68 | keyMetadata['datatype'] = 'Processed' # outgoing raster is now 'Processed'
69 | elif bandIndex == 0:
70 | keyMetadata['wavelengthmin'] = None # reset inapplicable band-specific key metadata
71 | keyMetadata['wavelengthmax'] = None
72 | return keyMetadata
73 |
--------------------------------------------------------------------------------
/functions/deprecated/CompositeBands.rft.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Composite
4 | A raster function template.
5 |
6 | Composite Band Function
7 | Combines rasters to form a multiband raster.
8 | UNKNOWN
9 |
10 |
11 | Raster[]
12 | The collection of all input rasters.
13 |
14 | false
15 |
16 |
17 | 2
18 |
19 |
20 | GroupName
21 | Tag
22 |
--------------------------------------------------------------------------------
/functions/deprecated/ConvertPerSecondToPerMonth.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | from datetime import datetime
3 | from calendar import monthrange
4 |
5 |
6 | class ConvertPerSecondToPerMonth():
7 |
8 | def __init__(self):
9 | self.name = "Convert Per-Second To Per-Month"
10 | self.description = ("This function converts a raster containing values "
11 | "in units-per-second to a raster representing units-per-month.")
12 | self.scaleFactor = 1.0
13 | self.unit = "units per month"
14 |
15 | def getParameterInfo(self):
16 | return [
17 | {
18 | 'name': 'raster',
19 | 'dataType': 'raster',
20 | 'value': None,
21 | 'required': True,
22 | 'displayName': "Raster",
23 | 'description': "The primary input raster."
24 | },
25 | {
26 | 'name': 'units',
27 | 'dataType': 'string',
28 | 'value': "per month",
29 | 'required': False,
30 | 'displayName': "Output Units",
31 | 'description': "Description of the units associated with the outgoing raster."
32 | },
33 | ]
34 |
35 | def getConfiguration(self, **scalars):
36 | return {
37 | 'compositeRasters': False, # input is a single raster, band compositing doesn't apply.
38 | 'inheritProperties': 2 | 4 | 8, # inherit all but the pixel type
39 | 'invalidateProperties': 2 | 4 | 8, # reset statistics and histogram
40 | 'keyMetadata': ('stdtime', 'acquisitiondate'), # we can use this key property in .updateRasterInfo()
41 | }
42 |
43 | def updateRasterInfo(self, **kwargs):
44 | kwargs['output_info']['statistics'] = ()
45 | kwargs['output_info']['histogram'] = ()
46 | if kwargs['raster_info']['pixelType'] != 'f8':
47 | kwargs['output_info']['pixelType'] = 'f4'
48 |
49 | d = kwargs['raster_keyMetadata'].get('acquisitiondate', None)
50 | d = kwargs['raster_keyMetadata'].get('stdtime', d)
51 | if d is None:
52 | raise Exception("Unable to obtain date-time associated with the input raster using "
53 | "key metadata 'AcquisitionDate' or 'StdTime'.")
54 |
55 | dt, r = None, None
56 | if isinstance(d, float):
57 | dt = datetime.utcfromtimestamp((d - 25569.) * 86400.) # convert from variant time to Unix time
58 | elif isinstance(d, str) and d is not None and len(d) > 0:
59 | dt = datetime.strptime(d[:18], "%Y-%m-%dT%H:%M:%S")
60 |
61 | if dt is None:
62 | raise Exception("Unable to compute scale factor using the date '{0}' obtained from the input raster.".format(d))
63 |
64 | r = monthrange(dt.year, dt.month)
65 | if len(r) != 2 or not isinstance(r[1], int):
66 | raise Exception("Unable to compute scale factor using the date '{0}' obtained from the input raster.".format(d))
67 |
68 | self.scaleFactor = float(r[1]) * 86400.
69 | self.unit = kwargs.get('units', "per month")
70 | return kwargs
71 |
72 | def updatePixels(self, tlc, shape, props, **pixelBlocks):
73 | inBlock = pixelBlocks['raster_pixels']
74 | pixelBlocks['output_pixels'] = np.round(inBlock * self.scaleFactor).astype(props['pixelType'], copy=False)
75 | return pixelBlocks
76 |
77 | def updateKeyMetadata(self, names, bandIndex, **keyMetadata):
78 | if bandIndex == -1:
79 | keyMetadata['unit'] = self.unit
80 | return keyMetadata
81 |
--------------------------------------------------------------------------------
/functions/deprecated/ConvertPerSecondToPerMonth.rft.xml:
--------------------------------------------------------------------------------
1 |
2 | ConvertPerSecondToPerMonth
3 | A raster function template.
4 |
5 | Convert Per-Second To Per-Month
6 | This function converts a raster containing values in units-per-second to a raster representing units-per-month.
7 | UNKNOWN
8 |
9 |
10 |
11 | PythonModule
12 | ClassName
13 | raster
14 | units
15 |
16 |
17 | ConvertPerSecondToPerMonth.py
18 | ConvertPerSecondToPerMonth
19 |
20 | Raster
21 |
22 |
23 | true
24 |
25 |
26 | units
27 |
28 | per month
29 | false
30 |
31 |
32 |
33 |
34 | 1
35 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/functions/deprecated/DeviationFromMean.rft.xml:
--------------------------------------------------------------------------------
1 |
2 | Deviation from Mean
3 | Computes anomaly across a collection of rasters as indicated by the amount of deviation from the average.
4 |
5 | Arithmetic
6 | Adapter function for raster functions written in python.
7 | F32
8 |
9 |
10 |
11 | PythonModule
12 | ClassName
13 | op
14 | r1
15 | r2
16 | method
17 |
18 |
19 | Arithmetic.py
20 | Arithmetic
21 | Subtract
22 |
23 | Raster
24 |
25 | true
26 |
27 |
28 | Raster Function Template
29 | A raster function template.
30 |
31 | Aggregate
32 | Adapter function for raster functions written in python.
33 | F32
34 |
35 |
36 |
37 | PythonModule
38 | Rasters
39 | ClassName
40 | method
41 |
42 |
43 | Aggregate.py
44 |
45 | Raster[]
46 |
47 |
48 | false
49 |
50 | Aggregate
51 | Average
52 |
53 |
54 |
55 | Sum
56 |
57 |
58 |
59 | 2
60 |
61 |
62 | GroupName
63 | Tag
64 |
--------------------------------------------------------------------------------
/functions/deprecated/FocalStatistics.rft.xml:
--------------------------------------------------------------------------------
1 |
2 | FocalStatistics
3 | A raster function template.
4 |
5 | Focal Statistics
6 | A raster function written in Python.
7 | UNKNOWN
8 |
9 |
10 |
11 | PythonModule
12 | ClassName
13 | factor
14 | raster
15 |
16 |
17 | FocalStatistics.py
18 | FocalStatistics
19 | 5
20 |
21 | Raster
22 |
23 |
24 | true
25 |
26 |
27 |
28 |
29 | 0
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/functions/deprecated/HeatIndex.rft.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | HeatIndex
4 | A raster function template that combines ambient air temperature and relative humidity to return apparent temperature.
5 |
6 | HeatIndex
7 |
8 | F32
9 |
10 |
11 |
12 | PythonModule
13 | ClassName
14 | temperature
15 | rh
16 | units
17 | outunits
18 |
19 |
20 | HeatIndex.py
21 | HeatIndex
22 |
23 | Temperature
24 | A single-band raster where pixel values represent ambient air temperature.
25 |
26 | true
27 |
28 | Raster1
29 | t@SFC
30 |
31 |
32 |
33 | RH
34 | A single-band raster where pixel values represent relative humidity as a percentage value between 0 and 100.
35 |
36 | true
37 |
38 | Raster2
39 | rh@SFC
40 |
41 |
42 |
43 | Temperature-Units
44 | The unit of measurement associated with the input temperature raster.
45 | Fahrenheit
46 | false
47 |
48 |
49 | Output-Units
50 | The unit of measurement associated with the output heat-index raster.
51 | Fahrenheit
52 | false
53 |
54 |
55 |
56 |
57 | 2
58 |
59 |
60 | GroupName
61 | Tag
62 |
--------------------------------------------------------------------------------
/functions/deprecated/Hillshade-ScaleAdjusted-Py.rft.xml:
--------------------------------------------------------------------------------
1 |
2 | Scale-Adjusted Hillshade
3 | A raster function template.
4 |
5 | Hillshade Function
6 |
7 | U8
8 |
9 |
10 |
11 | PythonModule
12 | ClassName
13 | raster
14 | zf
15 | ce
16 | cf
17 |
18 |
19 | Hillshade.py
20 | Hillshade
21 |
22 | Raster
23 |
24 |
25 | true
26 |
27 |
28 | zf
29 |
30 | 1
31 | false
32 |
33 |
34 | ce
35 |
36 | 0.66400000000000003
37 | false
38 |
39 |
40 | cf
41 |
42 | 0.024
43 | false
44 |
45 |
46 |
47 |
48 | 0
49 |
50 |
51 |
52 |
53 |
--------------------------------------------------------------------------------
/functions/deprecated/KeyMetadata.py:
--------------------------------------------------------------------------------
1 | from utils import loadJSON
2 |
3 |
4 | class KeyMetadata():
5 |
6 | def __init__(self):
7 | self.name = "Key Metadata Function"
8 | self.description = "Override or insert key-metadata of a raster in a function chain."
9 | self.datasetProps = {}
10 | self.bandProps = []
11 |
12 | def getParameterInfo(self):
13 | return [
14 | {
15 | 'name': 'raster',
16 | 'dataType': 'raster',
17 | 'value': None,
18 | 'displayName': "Raster",
19 | 'required': True,
20 | 'description': "The primary raster input."
21 | },
22 | {
23 | 'name': 'property',
24 | 'dataType': 'string',
25 | 'value': '',
26 | 'displayName': "Property Name",
27 | 'required': False,
28 | 'description': "The name of the optional dataset-level key property to override."
29 | },
30 | {
31 | 'name': 'value',
32 | 'dataType': 'string',
33 | 'value': None,
34 | 'displayName': "Property Value",
35 | 'required': False,
36 | 'description': "The overriding new value of the dataset-level key property."
37 | },
38 | {
39 | 'name': 'bands',
40 | 'dataType': 'string',
41 | 'value': '',
42 | 'displayName': "Band Names",
43 | 'required': False,
44 | 'description': "A comma-separated string representing updated band names."
45 | },
46 | {
47 | 'name': 'json',
48 | 'dataType': 'string',
49 | 'value': '',
50 | 'displayName': "Metadata JSON",
51 | 'required': False,
52 | 'description': ("Key metadata to be injected into the outgoing raster described as a "
53 | "JSON string representing a collection of key-value pairs. "
54 | "Learn more by searching for 'Raster Key Properties' at http://resources.arcgis.com.")
55 | },
56 | ]
57 |
58 | def getConfiguration(self, **scalars):
59 | return {
60 | 'invalidateProperties': 8, # reset any key properties held by the parent function raster dataset
61 | }
62 |
63 | def updateRasterInfo(self, **kwargs):
64 | try:
65 | jsonInput = kwargs.get('json', "{}").strip()
66 | allProps = loadJSON(jsonInput) if jsonInput else {}
67 | except ValueError as e:
68 | raise Exception(e.message)
69 |
70 | self.datasetProps = { k.lower(): v for k, v in allProps.items() if k != 'bandproperties' }
71 |
72 | # inject name-value pair into bag of properties
73 | p = kwargs.get('property', "").lower()
74 | if p: self.datasetProps[p] = kwargs.get('value', None)
75 |
76 | # get bandproperties array from original JSON as a list of dictionaries...
77 | self.bandProps = []
78 | for d in allProps.get('bandproperties', []):
79 | self.bandProps.append(
80 | { k.lower(): v for k, v in d.items() } if isinstance(d, dict) else None)
81 |
82 | # ensure size of bandProps matches input band count
83 | bandCount = kwargs['raster_info']['bandCount']
84 | self.bandProps.extend([{} for k in range(0, bandCount-len(self.bandProps))])
85 |
86 | # inject band names into the bandProps dictionary
87 | bands = kwargs.get('bands', "").strip()
88 | if bands:
89 | bandNames = bands.split(',')
90 | for k in range(0, min(len(self.bandProps), len(bandNames))):
91 | b = bandNames[k].strip()
92 | if b: self.bandProps[k]['bandname'] = b
93 |
94 | return kwargs
95 |
96 | def updateKeyMetadata(self, names, bandIndex, **keyMetadata):
97 | # return keyMetadata dictionary with updated values for entries in [names]...
98 | properties = self.datasetProps if bandIndex == -1 else self.bandProps[bandIndex]
99 | if not properties:
100 | return keyMetadata
101 |
102 | skipCheck = not bool(names) # => key names are internally generated, not user-specified
103 | for k in (names or properties): # iterate over either of those containers
104 | if skipCheck or k in properties: # spend time checking for existence only if necessary
105 | v = properties[k]
106 | keyMetadata[str(k)] = str(v) if isinstance(v, unicode) else v
107 |
108 | return keyMetadata
109 |
--------------------------------------------------------------------------------
/functions/deprecated/LinearSpectralUnmixing.rft.xml:
--------------------------------------------------------------------------------
1 |
2 | Linear Spectral Unmixing
3 | Performs linear spectral unmixing for a multiband raster.
4 |
5 | Extract Band Function
6 | Reorders or extracts bands from a raster.
7 | UNKNOWN
8 |
9 |
10 |
11 | Raster
12 | BandIDs
13 | MissingBandAction
14 |
15 |
16 |
17 | Raster_2015228_53921_488
18 |
19 |
20 | Raster Function Template
21 | A raster function template.
22 |
23 | Linear Spectral Unmixing
24 | Performs linear spectral unmixing for a multiband raster.
25 | UNKNOWN
26 |
27 |
28 |
29 | raster
30 | signatures
31 | method
32 | PythonModule
33 | ClassName
34 |
35 |
36 |
37 | Raster
38 |
39 |
40 | true
41 |
42 |
43 | Signatures
44 |
45 | {"Shadow": [70.05629, 27.24081, 25.31275, 24.17432, 31.77904, 17.82422], "Veg": [65.46086, 30.09995, 26.27376, 117.45741, 76.96012, 26.25062], "NPV": [74.74029, 32.06931, 35.57350, 32.66032, 73.63062, 60.51104], "Soil": [143.65580, 79.30271, 102.82176, 93.60246, 176.57705, 117.49280]}
46 | false
47 |
48 |
49 | Method
50 |
51 | Scaled
52 | false
53 |
54 | LinearSpectralUnmixing.py
55 | LinearSpectralUnmixing
56 |
57 |
58 |
59 | 0
60 |
61 |
62 |
63 |
64 |
65 | true
66 |
67 |
68 | BandIDs_2015228_53921_488
69 |
70 |
71 | 0
72 |
73 | false
74 |
75 |
76 | MissingBandAction_2015228_53921_488
77 |
78 | 0
79 | false
80 |
81 |
82 |
83 |
84 | 0
85 |
86 |
87 |
88 |
89 |
--------------------------------------------------------------------------------
/functions/deprecated/MergeRasters.rft.xml:
--------------------------------------------------------------------------------
1 |
2 | MergeRasters
3 | Merges a collection of rasters on the fly.
4 |
5 | Merge Rasters Function
6 | Merges a collection of rasters on the fly.
7 | UNKNOWN
8 |
9 |
10 |
11 | Rasters
12 |
13 |
14 |
15 | Raster[]
16 |
17 |
18 | false
19 |
20 |
21 |
22 |
23 | 0
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/functions/deprecated/MultidirectionalHillshade.rft.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Multidirectional Hillshade
4 | A raster function template.
5 |
6 | Multidirectional Hillshade
7 | Adapter function for raster functions written in python.
8 | F32
9 |
10 |
11 |
12 | PythonModule
13 | raster
14 |
15 |
16 | MultidirectionalHillshade.pyd
17 |
18 | Raster
19 |
20 |
21 | true
22 |
23 |
24 |
25 |
26 | 0
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/functions/deprecated/NDVI-Colormap.rft.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | NDVI-Colormap
4 | A raster function template.
5 |
6 | NDVI
7 | Adapter function for raster functions written in python.
8 | F32
9 |
10 |
11 |
12 | PythonModule
13 | raster
14 | red
15 | ir
16 | method
17 |
18 |
19 | NDVI.py
20 |
21 | Raster
22 |
23 |
24 | true
25 |
26 | 3
27 | 4
28 | Colormap
29 |
30 |
31 |
32 | 0
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/functions/deprecated/NDVI-Grayscale.rft.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | NDVI-Grayscale
4 | A raster function template.
5 |
6 | NDVI
7 | Adapter function for raster functions written in python.
8 | F32
9 |
10 |
11 |
12 | PythonModule
13 | raster
14 | red
15 | ir
16 | method
17 |
18 |
19 | NDVI.py
20 |
21 | Raster
22 |
23 |
24 | true
25 |
26 | 3
27 | 4
28 | Grayscale
29 |
30 |
31 |
32 | 0
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/functions/deprecated/NDVI.rft.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | NDVI-Raw
4 | A raster function template.
5 |
6 | NDVI
7 | Adapter function for raster functions written in python.
8 | F32
9 |
10 |
11 |
12 | PythonModule
13 | raster
14 | red
15 | ir
16 | method
17 |
18 |
19 | NDVI.py
20 |
21 | Raster
22 |
23 |
24 | true
25 |
26 | 3
27 | 4
28 | Raw
29 |
30 |
31 |
32 | 0
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/functions/deprecated/Random.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 |
3 |
4 | class Random():
5 |
6 | def __init__(self):
7 | self.name = "Random Raster Function"
8 | self.description = ""
9 |
10 |
11 | def getParameterInfo(self):
12 | return []
13 |
14 |
15 | def getConfiguration(self, **scalars):
16 | return {
17 | 'inheritProperties': 0, # no input raster, nothing to inherit.
18 | 'invalidateProperties': 1 | 2 | 4 | 8, # reset everything on the parent dataset.
19 | 'resampling': True,
20 | }
21 |
22 |
23 | def updateRasterInfo(self, **kwargs):
24 | nBands = 3
25 | minX, minY, maxX, maxY = 0.0, 0.0, 1000.0, 1000.0
26 | dX, dY = 10.0, 10.0
27 | sr = 3857
28 |
29 | outputInfo = {
30 | 'bandCount': nBands,
31 | 'pixelType': 'u1',
32 | 'nativeExtent': (minX, minY, maxX, maxY),
33 | 'extent': (minX, minY, maxX, maxY),
34 | 'cellSize': (dX, dY),
35 | 'spatialReference': sr,
36 | 'nativeSpatialReference': sr,
37 | 'statistics': (),
38 | 'histogram': (),
39 | 'colormap': (),
40 | 'noData': np.array([256], dtype='u1'),
41 | }
42 |
43 | kwargs['output_info'] = outputInfo
44 | return kwargs
45 |
46 |
47 | def updatePixels(self, tlc, shape, props, **pixelBlocks):
48 | outBlock = 255.0 * np.random.random_sample(shape)
49 | pixelBlocks['output_pixels'] = outBlock.astype(props['pixelType'])
50 | pixelBlocks['output_mask'] = np.ones(shape).astype('u1')
51 | return pixelBlocks
52 |
--------------------------------------------------------------------------------
/functions/deprecated/Random.rft.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Random
4 | A raster function template.
5 |
6 | Random
7 | Adapter function for raster functions written in python.
8 |
9 |
10 |
11 |
12 | PythonModule
13 |
14 |
15 | Random.py
16 |
17 |
18 |
19 | 0
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/functions/deprecated/Subtract.rft.xml:
--------------------------------------------------------------------------------
1 |
2 | Subtract
3 | A raster function template that returns the difference of two rasters.
4 |
5 | Arithmetic Function
6 | Performs an arithmetic operation between two partially or completely spatially overlapping rasters or a raster and one or more constant values.
7 | UNKNOWN
8 |
9 |
10 |
11 | Raster
12 | Raster2
13 | Operation
14 | ExtentType
15 | CellsizeType
16 | RasterToGenerate
17 |
18 |
19 |
20 | Raster1
21 |
22 |
23 | true
24 |
25 |
26 | Raster2
27 |
28 |
29 | true
30 |
31 |
32 | Operation
33 |
34 | 2
35 | false
36 |
37 |
38 | ExtentType
39 |
40 | 1
41 | false
42 |
43 |
44 | CellsizeType
45 |
46 | 1
47 | false
48 |
49 |
50 | RasterToGenerate
51 |
52 | 0
53 | false
54 |
55 |
56 |
57 |
58 | 2
59 |
60 |
61 | GroupName
62 | Tag
63 |
64 |
--------------------------------------------------------------------------------
/functions/deprecated/Windchill.rft.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Windchill
4 | A raster function template that computes windchill given rasters representing wind-speed and temperature.
5 |
6 | Windchill
7 |
8 | F32
9 |
10 |
11 |
12 | PythonModule
13 | ClassName
14 | temperature
15 | ws
16 | tunits
17 | wunits
18 | ounits
19 |
20 |
21 | Windchill.py
22 | Windchill
23 |
24 | Temperature
25 |
26 |
27 | true
28 |
29 | Raster1
30 | t@SFC
31 |
32 |
33 |
34 | WS
35 |
36 |
37 | true
38 |
39 | Raster2
40 | Windspeed
41 | windspd@SFC
42 |
43 |
44 |
45 | Temperature-Units
46 |
47 | Fahrenheit
48 | false
49 |
50 |
51 | Windspeed-Units
52 |
53 | mph
54 | false
55 |
56 |
57 | Output-Units
58 |
59 | Fahrenheit
60 | false
61 |
62 |
63 |
64 |
65 | 2
66 |
67 |
68 | GroupName
69 | Variable
70 |
--------------------------------------------------------------------------------
/functions/deprecated/ZonalRemap.rft.xml:
--------------------------------------------------------------------------------
1 |
2 | ZonalRemap
3 |
4 |
5 | Zonal Remap
6 | A raster function written in Python.
7 | UNKNOWN
8 |
9 |
10 |
11 | PythonModule
12 | ClassName
13 | vraster
14 | zraster
15 | ztable
16 | zid
17 | zmin
18 | zmax
19 | zval
20 | background
21 | defzval
22 | where
23 |
24 |
25 | ZonalRemap.py
26 | ZonalRemap
27 |
28 | Raster
29 |
30 |
31 |
32 | Raster1
33 |
34 | true
35 |
36 |
37 | ZoneRaster
38 |
39 |
40 |
41 | Raster2
42 |
43 | true
44 |
45 |
46 | ztable
47 |
48 |
49 | false
50 |
51 |
52 | zid
53 |
54 | ZoneID
55 | false
56 |
57 |
58 | zmin
59 |
60 | ZoneMin
61 | false
62 |
63 |
64 | zmax
65 |
66 | ZoneMax
67 | false
68 |
69 |
70 | zval
71 |
72 | ZoneValue
73 | false
74 |
75 |
76 | background
77 |
78 | 0
79 | false
80 |
81 |
82 | defzval
83 |
84 | 255
85 | false
86 |
87 |
88 | where
89 |
90 |
91 | false
92 |
93 |
94 |
95 |
96 | 2
97 |
98 |
99 | GroupName
100 | Tag
101 |
102 |
103 |
--------------------------------------------------------------------------------
/functions/functions.pyproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Debug
5 | 2.0
6 | {4842f09b-120b-4468-ae7c-f48cb0adf7bb}
7 |
8 | KeyMetadata.py
9 |
10 | .
11 | .
12 | {888888a0-9f3d-457c-b088-3a5042f75d52}
13 | Standard Python launcher
14 |
15 |
16 |
17 |
18 |
19 |
20 | 10.0
21 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\Python Tools\Microsoft.PythonTools.targets
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
--------------------------------------------------------------------------------
/raster-functions.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 2013
4 | VisualStudioVersion = 12.0.31101.0
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{888888A0-9F3D-457C-B088-3A5042F75D52}") = "functions", "functions\functions.pyproj", "{4842F09B-120B-4468-AE7C-F48CB0ADF7BB}"
7 | EndProject
8 | Global
9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
10 | Debug|Any CPU = Debug|Any CPU
11 | Release|Any CPU = Release|Any CPU
12 | EndGlobalSection
13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
14 | {4842F09B-120B-4468-AE7C-F48CB0ADF7BB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
15 | {4842F09B-120B-4468-AE7C-F48CB0ADF7BB}.Release|Any CPU.ActiveCfg = Release|Any CPU
16 | EndGlobalSection
17 | GlobalSection(SolutionProperties) = preSolution
18 | HideSolutionNode = FALSE
19 | EndGlobalSection
20 | EndGlobal
21 |
--------------------------------------------------------------------------------
/scripts/ExtractRasterInfo.py:
--------------------------------------------------------------------------------
1 | import arcpy
2 | import arcpy.sa as sa
3 | from glob import glob
4 |
5 | print("ObjectID,Raster,NCols,NRows,NBands,PixelType,XMin,YMin,XMax,YMax,SRS")
6 |
7 | for k, f in enumerate(glob(r"e:\raster-types\Data\Tiles\*.tif"), 1):
8 | r = sa.Raster(f)
9 | e = r.extent
10 | print(",".join((str(k), str(f), str(r.width), str(r.height), str(r.bandCount), str(r.pixelType), str(e.XMin), str(e.YMin), str(e.XMax), str(e.YMax), str(r.spatialReference.factoryCode))))
11 |
--------------------------------------------------------------------------------
/scripts/requirements.txt:
--------------------------------------------------------------------------------
1 | pyparsing
2 | pyproj
3 | python-dateutil
4 | pytz
5 | six
6 | numpy
7 | scipy
8 | matplotlib
9 | scikit-image
10 | scikit-learn
11 | Cython
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | '''
2 | ====================================================================================
3 | setup.py: Automated installation of dependencies required for Python raster function
4 | ====================================================================================
5 |
6 | Installation
7 | ------------
8 |
9 | To execute **setup.py** within the package directory run:
10 | $ python setup.py
11 | '''
12 |
13 | import sys
14 | import logging
15 | from os import path, makedirs
16 | from subprocess import call
17 | from time import sleep
18 |
19 |
20 | '''
21 | ErrorLevel on exit:
22 | 0 : Installation successful.
23 | 1 : PIP installation unsuccessful.
24 | 2 : File cannot be downloaded.
25 | 4 : VC++ Compiler for Python installation failed.
26 | 5 : Requirements.txt file not found.
27 | 6 : Python package installation failed.
28 | 99 : ArcGIS 10.3.1 or above not found.
29 | '''
30 |
31 | def log(s):
32 | print(">>> {0}".format(s))
33 |
34 |
35 | def die(errorLog, errorCode):
36 | print("\n\n")
37 | logging.error(errorLog)
38 | print("\n")
39 | sleep(2)
40 | exit(errorCode)
41 |
42 |
43 | def downloadFile(url, filePath):
44 | try:
45 | log("Downloading: {0} to {1}".format(url, filePath))
46 |
47 | try:
48 | from urllib2 import urlopen
49 | except:
50 | from urllib.request import urlopen
51 |
52 | d = path.dirname(filePath)
53 | if not path.exists(d):
54 | makedirs(d)
55 |
56 | with open(filePath, 'wb') as f:
57 | f.write(urlopen(url).read())
58 | except:
59 | die("Unable to download URL", 2)
60 |
61 |
62 | def locateFile(url, filePath):
63 | if not path.isfile(filePath):
64 | downloadFile(url, filePath)
65 | log("Located: {0}".format(filePath))
66 |
67 |
68 | def main():
69 | pipURL = "http://bootstrap.pypa.io/get-pip.py"
70 | vcURL = "http://download.microsoft.com/download/7/9/6/796EF2E4-801B-4FC4-AB28-B59FBF6D907B/VCForPython27.msi"
71 |
72 | pipExePath = path.join(path.dirname(sys.executable), r"Scripts\pip.exe")
73 | setupHome = path.join(path.abspath(path.dirname(__file__)), "scripts")
74 | distHome = path.join(path.abspath(path.dirname(__file__)), "dist")
75 |
76 | try:
77 | log("Installing PIP")
78 | pipPyPath = path.join(setupHome, "get-pip.py")
79 | locateFile(pipURL, pipPyPath)
80 | call([sys.executable, pipPyPath])
81 |
82 | if path.isfile(pipExePath):
83 | log("PIP installed successfully")
84 | else:
85 | raise Exception("PIP failed")
86 |
87 | call([pipExePath, "install", "--upgrade", "--no-index", "--find-links={0}".format(distHome), "pip"])
88 | call([pipExePath, "install", "--upgrade", "--no-index", "--find-links={0}".format(distHome), "wheel"])
89 | except:
90 | die("PIP installation failed!", 1)
91 |
92 | try:
93 | if sys.version_info[0] == 2:
94 | log("Installing Microsoft Visual C++ Compiler")
95 | vcSetupPath = path.join(distHome, "VCForPython27.msi")
96 | locateFile(vcURL, vcSetupPath)
97 | c = ["msiexec", "/i", vcSetupPath, "/qb-"]
98 | log("Executing: {0}".format(" ".join(c)))
99 | call(c)
100 | log("C++ Compiler for Python installed successfully")
101 | except:
102 | die("VC++ Compiler for Python installation failed!.", 4)
103 |
104 | try:
105 | log("Installing Python dependencies")
106 | reqFilePath = path.join(setupHome, "requirements.txt")
107 | if not path.isfile(reqFilePath):
108 | die("Dependency listing file not found: {0}".format(reqFilePath), 5)
109 |
110 | c = [pipExePath, "install", "--upgrade", "--no-index", "--find-links={0}".format(distHome), "-r", reqFilePath]
111 | log("Executing: {0}".format(" ".join(c)))
112 | call(c)
113 | except:
114 | die("Dependency installation failed!", 6)
115 |
116 | try:
117 | arcpy = __import__('arcpy')
118 | info = arcpy.GetInstallInfo()
119 |
120 | bVersionOK = True
121 |
122 | minArcGISVersion = '10.3.1'
123 | if info['Version'].split(".")[0] == 10 and (tuple(map(int, (info['Version'].split(".")))) < tuple(map(int, (minArcGISVersion.split("."))))):
124 | bVersionOK = False
125 |
126 | minProVersion = '1.0'
127 | if info['Version'].split(".")[0] == 1 and (tuple(map(int, (info['Version'].split(".")))) < tuple(map(int, (minProVersion.split("."))))):
128 | bVersionOK = False
129 |
130 | if not bVersionOK:
131 | raise Exception("No ArcGIS")
132 |
133 | print("\n\n")
134 | if info['Version'][0] == 10:
135 | log("Python extensions for raster functions in ArcGIS {} {} build {} successfully installed.".format(
136 | info['ProductName'], info['Version'], info['BuildNumber']))
137 | else:
138 | log("Python extensions for raster functions in {} {} build {} successfully installed.".format(
139 | info['ProductName'], info['Version'], info['BuildNumber']))
140 | except:
141 | logging.warn("Unable to find ArcGIS 10.3.1/ArcGIS Pro 1.0 or above.")
142 |
143 | log("Done.")
144 | sleep(2)
145 | exit(0)
146 |
147 |
148 | if __name__ == '__main__':
149 | main()
150 |
151 |
152 | # Uninstall using: pip uninstall --yes -r requirements.txt
--------------------------------------------------------------------------------