| | 1722 | def ProjectToTemplate(cls, inputRaster, templateRaster, outputRaster, resamplingTechnique, interpolationMethod=None, maxHoleSize=None, mask=False, overwriteExisting=False): |
| | 1723 | try: |
| | 1724 | # Perform additional validation. |
| | 1725 | |
| | 1726 | if interpolationMethod is not None and resamplingTechnique.lower() not in [u'bilinear', u'cubic']: |
| | 1727 | raise ValueError(_(u'Values cannot be interpolated for No Data cells if the NEAREST or MAJORITY resampling technique is used. Please select the BILINEAR or CUBIC resampling technique instead.')) |
| | 1728 | |
| | 1729 | # Look up the coordinate system, extent, and cell size of |
| | 1730 | # the template raster. |
| | 1731 | |
| | 1732 | gp = GeoprocessorManager.GetWrappedGeoprocessor() |
| | 1733 | d = gp.Describe(templateRaster) |
| | 1734 | coordinateSystem = gp.CreateSpatialReference(d.SpatialReference).split(';')[0] |
| | 1735 | extent = d.Extent |
| | 1736 | cellSize = d.MeanCellWidth |
| | 1737 | |
| | 1738 | # Below, we will set the Extent environment variable and |
| | 1739 | # then call ArcGIS's ProjectRaster_management to |
| | 1740 | # simultaneously project and clip the input raster to the |
| | 1741 | # coordinate system, extent, and cell size of the template |
| | 1742 | # raster. Unfortunately ProjectRaster_management will not |
| | 1743 | # necessarily create a raster that has exactly the desired |
| | 1744 | # extent. It may be one cell larger or smaller in any of |
| | 1745 | # the four directions. Different versions of ArcGIS seem |
| | 1746 | # to work differently in this respect. |
| | 1747 | # |
| | 1748 | # To deal with this annoyance, we will expand the extent |
| | 1749 | # in all four directions by 10 cells--guaranteeing that we |
| | 1750 | # have a raster that is larger than the desired |
| | 1751 | # extent--and then use ExtractByMask_sa to obtain a raster |
| | 1752 | # of the desired extent. |
| | 1753 | # |
| | 1754 | # We use 10 cells rather than 1 cell because of a |
| | 1755 | # different problem: if the caller requested that we |
| | 1756 | # interpolate values for No Data cells, the most accurate |
| | 1757 | # values can be obtained if each No Data region is |
| | 1758 | # completely surrounded by cells with data. In the event |
| | 1759 | # that the template raster extent bisects a No Data |
| | 1760 | # region, the 10 cell buffer increases the chance that we |
| | 1761 | # will interpolate that region using cells from both sides |
| | 1762 | # of the extent line: the side that is within the template |
| | 1763 | # extent and also the side that is outside the extent but |
| | 1764 | # within the buffer. |
| | 1765 | |
| | 1766 | from GeoEco.DataManagement.Directories import TemporaryDirectory |
| | 1767 | tempDir = TemporaryDirectory() |
| | 1768 | |
| | 1769 | [left, bottom, right, top] = EnvelopeTypeMetadata.ParseFromArcGISString(extent) |
| | 1770 | oldExtent = gp.Extent |
| | 1771 | gp.Extent = '%r %r %r %r' % (left - cellSize*10, bottom - cellSize*10, right + cellSize*10, top + cellSize*10) |
| | 1772 | |
| | 1773 | try: |
| | 1774 | # If this is ArcGIS 9.3, set the SnapRaster |
| | 1775 | # environment variable to the template raster. |
| | 1776 | |
| | 1777 | if GeoprocessorManager.GetArcGISMajorVersion() > 9 or GeoprocessorManager.GetArcGISMajorVersion() == 9 and GeoprocessorManager.GetArcGISMinorVersion() >= 3: |
| | 1778 | oldSnapRaster = gp.SnapRaster |
| | 1779 | gp.SnapRaster = templateRaster |
| | 1780 | |
| | 1781 | # Project the raster. If this is ArcGIS 9.3 or later, |
| | 1782 | # the SnapRaster and Extent environment variables will |
| | 1783 | # ensure the projected cells are snapped and clipped |
| | 1784 | # as requested by the caller. If it is 9.2, use the |
| | 1785 | # Registration_Point parameter instead of SnapRaster, |
| | 1786 | # which did not exist in 9.2 (there was something like |
| | 1787 | # it, but it did not work correctly). |
| | 1788 | |
| | 1789 | try: |
| | 1790 | projectedRaster = os.path.join(tempDir.Path, u'projected.img') |
| | 1791 | if GeoprocessorManager.GetArcGISMajorVersion() > 9 or GeoprocessorManager.GetArcGISMajorVersion() == 9 and GeoprocessorManager.GetArcGISMinorVersion() >= 3: |
| | 1792 | gp.ProjectRaster_management(inputRaster, projectedRaster, coordinateSystem, resamplingTechnique, cellSize) |
| | 1793 | else: |
| | 1794 | gp.ProjectRaster_management(inputRaster, projectedRaster, coordinateSystem, resamplingTechnique, cellSize, None, extent.rsplit(' ', 2)[0]) |
| | 1795 | |
| | 1796 | # If this is ArcGIS 9.3, reset the SnapRaster |
| | 1797 | # environment variable to what it was before. |
| | 1798 | |
| | 1799 | finally: |
| | 1800 | if GeoprocessorManager.GetArcGISMajorVersion() > 9 or GeoprocessorManager.GetArcGISMajorVersion() == 9 and GeoprocessorManager.GetArcGISMinorVersion() >= 3: |
| | 1801 | gp.SnapRaster = oldSnapRaster |
| | 1802 | |
| | 1803 | # Reset the Extent ArcGIS environment variable to what it |
| | 1804 | # was before. |
| | 1805 | |
| | 1806 | finally: |
| | 1807 | gp.Extent = oldExtent |
| | 1808 | |
| | 1809 | # If the caller requested that we interpolate values for |
| | 1810 | # No Data regions, do it now. |
| | 1811 | |
| | 1812 | if interpolationMethod is not None: |
| | 1813 | from GeoEco.SpatialAnalysis.Interpolation import Interpolator |
| | 1814 | infilledRaster = os.path.join(tempDir.Path, u'infilled.img') |
| | 1815 | Interpolator.InpaintArcGISRaster(projectedRaster, infilledRaster, interpolationMethod, maxHoleSize) |
| | 1816 | projectedRaster = infilledRaster |
| | 1817 | |
| | 1818 | # We are about to use ExtractByMask_sa to extract a raster |
| | 1819 | # of the desired extent from the projected raster in the |
| | 1820 | # temp directory. But ExtractByMask_sa also has a side |
| | 1821 | # effect that we might not want: it sets cells that are No |
| | 1822 | # Data in the mask to No Data in the output raster. If the |
| | 1823 | # caller did not request that to happen (using the |
| | 1824 | # template raster as the mask), create a constant raster |
| | 1825 | # that has the same coordinate system, extent, and cell |
| | 1826 | # size as the template raster. We'll use it instead. |
| | 1827 | |
| | 1828 | if not mask: |
| | 1829 | oldOutputCoordinateSystem = gp.OutputCoordinateSystem |
| | 1830 | gp.OutputCoordinateSystem = coordinateSystem |
| | 1831 | try: |
| | 1832 | maskRaster = os.path.join(tempDir.Path, u'constant.img') |
| | 1833 | gp.CreateConstantRaster_sa(maskRaster, 0, 'INTEGER', cellSize, extent) |
| | 1834 | finally: |
| | 1835 | gp.OutputCoordinateSystem = oldOutputCoordinateSystem |
| | 1836 | |
| | 1837 | # For safety, verify that the output raster has the |
| | 1838 | # expected number of columns and rows. |
| | 1839 | |
| | 1840 | d2 = gp.Describe(maskRaster) |
| | 1841 | if d2.Width != d.Width or d2.Height != d.Height: |
| | 1842 | raise RuntimeError(_(u'Programming error in this tool: the constant raster does not have the same number of rows or columns as the template raster. Please contact the author of this tool for assistance.')) |
| | 1843 | |
| | 1844 | else: |
| | 1845 | maskRaster = templateRaster |
| | 1846 | |
| | 1847 | # Extract the raster. |
| | 1848 | |
| | 1849 | gp.ExtractByMask_sa(projectedRaster, maskRaster, outputRaster) |
| | 1850 | |
| | 1851 | except: |
| | 1852 | Logger.LogExceptionAsError() |
| | 1853 | raise |
| | 1854 | |
| | 1855 | @classmethod |
| | 3062 | |
| | 3063 | # Public method: ArcGISRaster.ProjectToTemplate |
| | 3064 | |
| | 3065 | AddMethodMetadata(ArcGISRaster.ProjectToTemplate, |
| | 3066 | shortDescription=_(u'Projects a raster to the coordinate system, cell size, and extent of a template raster.'), |
| | 3067 | isExposedToPythonCallers=True, |
| | 3068 | isExposedByCOM=True, |
| | 3069 | isExposedAsArcGISTool=True, |
| | 3070 | arcGISDisplayName=_(u'Project Raster to Template'), |
| | 3071 | arcGISToolCategory=_(u'Spatial Analysis\\Project Raster to Template'), |
| | 3072 | dependencies=[ArcGISDependency(9, 1)]) |
| | 3073 | |
| | 3074 | CopyArgumentMetadata(ArcGISRaster.Copy, u'cls', ArcGISRaster.ProjectToTemplate, u'cls') |
| | 3075 | |
| | 3076 | AddArgumentMetadata(ArcGISRaster.ProjectToTemplate, u'inputRaster', |
| | 3077 | typeMetadata=ArcGISRasterLayerTypeMetadata(mustExist=True), |
| | 3078 | description=_( |
| | 3079 | u"""Raster to project to the template raster's coordinate system, cell |
| | 3080 | size, and extent."""), |
| | 3081 | arcGISDisplayName=_(u'Raster to project')) |
| | 3082 | |
| | 3083 | AddArgumentMetadata(ArcGISRaster.ProjectToTemplate, u'templateRaster', |
| | 3084 | typeMetadata=ArcGISRasterLayerTypeMetadata(mustExist=True, mustBeDifferentThanArguments=[u'inputRaster']), |
| | 3085 | description=_( |
| | 3086 | u"""Raster defining the coordinate system, cell size, and extent of |
| | 3087 | the output raster."""), |
| | 3088 | arcGISDisplayName=_(u'Template raster')) |
| | 3089 | |
| | 3090 | AddArgumentMetadata(ArcGISRaster.ProjectToTemplate, u'outputRaster', |
| | 3091 | typeMetadata=ArcGISRasterTypeMetadata(mustBeDifferentThanArguments=[u'inputRaster', u'templateRaster'], deleteIfParameterIsTrue=u'overwriteExisting', createParentDirectories=True), |
| | 3092 | description=_( |
| | 3093 | u"""Output raster. |
| | 3094 | |
| | 3095 | If this path refers to the file system, missing directories in the |
| | 3096 | path will be created if they do not exist."""), |
| | 3097 | direction=u'Output', |
| | 3098 | arcGISDisplayName=_(u'Output raster')) |
| | 3099 | |
| | 3100 | AddArgumentMetadata(ArcGISRaster.ProjectToTemplate, u'resamplingTechnique', |
| | 3101 | typeMetadata=UnicodeStringTypeMetadata(makeLowercase=True, allowedValues=[u'BILINEAR', u'CUBIC']), |
| | 3102 | description=_( |
| | 3103 | u"""Resampling algorithm to be used to project the input raster to the |
| | 3104 | template raster's coordinate system. One of: |
| | 3105 | |
| | 3106 | * NEAREST - `Nearest neighbor assignment |
| | 3107 | <http://en.wikipedia.org/wiki/Nearest-neighbor_interpolation>`_. |
| | 3108 | |
| | 3109 | * BILINEAR - `Bilinear interpolation |
| | 3110 | <http://en.wikipedia.org/wiki/Bilinear_interpolation>`_. |
| | 3111 | |
| | 3112 | * CUBIC - Cubic convolution, also known as `bicubic interpolation |
| | 3113 | <http://en.wikipedia.org/wiki/Bicubic_interpolation>`_. |
| | 3114 | |
| | 3115 | * MAJORITY - Majority resampling. This method requires ArcGIS 9.3 or |
| | 3116 | later. |
| | 3117 | |
| | 3118 | The NEAREST and MAJORITY algorithms should be used for categorical |
| | 3119 | data, such as a land use classification. It is not recommended that |
| | 3120 | NEAREST or MAJORITY be used for continuous data, such as elevation |
| | 3121 | surfaces. |
| | 3122 | |
| | 3123 | The BILINEAR and CUBIC options are most appropriate for continuous |
| | 3124 | data. Do not use BILINEAR or CUBIC with categorical data. |
| | 3125 | |
| | 3126 | The projection is accomplished with the ArcGIS Project Raster tool. |
| | 3127 | Please see the documentation for that tool for more information."""), |
| | 3128 | arcGISDisplayName=_(u'Resampling technique')) |
| | 3129 | |
| | 3130 | AddArgumentMetadata(ArcGISRaster.ProjectToTemplate, u'interpolationMethod', |
| | 3131 | typeMetadata=UnicodeStringTypeMetadata(makeLowercase=True, allowedValues=[u'Del2a', u'Del2b', u'Del2c', u'Del4', u'Spring'], canBeNone=True), |
| | 3132 | description=_( |
| | 3133 | u"""Method to use to guess values for No Data cells. |
| | 3134 | |
| | 3135 | Use this option to "fill in" clusters of No Data cells with values |
| | 3136 | obtained by interpolation and extrapolation. This option is |
| | 3137 | appropriate for rasters representing continuous surfaces, e.g. images |
| | 3138 | of sea surface temperature in which cloudy pixels contain No Data. It |
| | 3139 | uses algorithms based on differential calculus that may provide more |
| | 3140 | accurate guesses than traditional ArcGIS approaches, such as computing |
| | 3141 | the focal mean of a 3x3 neighborhood. |
| | 3142 | |
| | 3143 | This option is not appropriate for rasters representing categorical |
| | 3144 | data, such as land cover classifications. Therefore, in order to use |
| | 3145 | this option, you must select BILINEAR or CUBIC for the Resampling |
| | 3146 | Technique parameter. |
| | 3147 | |
| | 3148 | The available algorithms are: |
| | 3149 | |
| | 3150 | * Del2a - Laplacian interpolation and linear extrapolation. |
| | 3151 | |
| | 3152 | * Del2b - Same as Del2a but does not build as large a linear system of |
| | 3153 | equations. May be faster than Del2a at the cost of some accuracy. |
| | 3154 | |
| | 3155 | * Del2c - Same as Del2a but solves a direct linear system of equations |
| | 3156 | for the No Data values. Faster than both Del2a and Del2b but is the |
| | 3157 | least robust to noise on the boundaries of No Data cells and least |
| | 3158 | able to interpolate accurately for smooth surfaces. |
| | 3159 | |
| | 3160 | * Del4 - Same as Del2a but instead of the Laplace operator (also |
| | 3161 | called the del^2 operator) it uses the biharmonic operator (also |
| | 3162 | called the del^4 operator). May result in more accurate |
| | 3163 | interpolations, at some cost in speed. |
| | 3164 | |
| | 3165 | * Spring - Uses a spring metaphor. Assumes springs (with a nominal |
| | 3166 | length of zero) connect each cell with every neighbor (horizontally, |
| | 3167 | vertically and diagonally). Since each cell tries to be like its |
| | 3168 | neighbors, extrapolation is as a constant function where this is |
| | 3169 | consistent with the neighboring nodes. |
| | 3170 | |
| | 3171 | This option is applied after the input raster has been projected to |
| | 3172 | the coordinate system and cell size of the template raster. |
| | 3173 | |
| | 3174 | Although this tool can fill No Data clusters of any size, you should |
| | 3175 | apply common sense when using it. The larger the cluster, the less |
| | 3176 | accurate the guessed values will be, especially for rasters that |
| | 3177 | represent a noisy surface. |
| | 3178 | |
| | 3179 | Thanks to John D'Errico for providing the code that implements the |
| | 3180 | mathematical algorithms described here (click `here |
| | 3181 | <http://www.mathworks.com/matlabcentral/fileexchange/4551>`_ for more |
| | 3182 | information)."""), |
| | 3183 | arcGISDisplayName=_(u'Method for interpolating No Data cells'), |
| | 3184 | arcGISCategory=_(u'Interpolation and masking options')) |
| | 3185 | |
| | 3186 | AddArgumentMetadata(ArcGISRaster.ProjectToTemplate, u'maxHoleSize', |
| | 3187 | typeMetadata=IntegerTypeMetadata(mustBeGreaterThan=0, canBeNone=True), |
| | 3188 | description=_( |
| | 3189 | u"""Maximum size, in cells of the template raster, that a region of |
| | 3190 | 4-connected No Data cells may be for it to be filled in. |
| | 3191 | |
| | 3192 | Use this option to prevent the filling of large No Data regions (e.g. |
| | 3193 | large clouds in remote sensing images) when you are concerned that |
| | 3194 | values cannot be accurately guessed for those regions. If this option |
| | 3195 | is omitted, all regions will be filled, regardless of size."""), |
| | 3196 | arcGISDisplayName=_(u'Maximum size of No Data regions to interpolate'), |
| | 3197 | arcGISCategory=_(u'Interpolation and masking options')) |
| | 3198 | |
| | 3199 | AddArgumentMetadata(ArcGISRaster.ProjectToTemplate, u'mask', |
| | 3200 | typeMetadata=BooleanTypeMetadata(), |
| | 3201 | description=_( |
| | 3202 | u"""If True, the template raster will be used to mask the input raster |
| | 3203 | after it has been projected to the template raster's coordinate system |
| | 3204 | and values have been interpolated for No Data cells (if your requested |
| | 3205 | that). Cells of the template raster that are No Data will be set to No |
| | 3206 | Data in the output raster, even if you requested that values be |
| | 3207 | interpolated for No Data cells. This is appropriate in situations |
| | 3208 | where the template defines the areas for which you want to retain |
| | 3209 | data; for example, when you are analyzing the ocean and you have a |
| | 3210 | mask in which ocean cells have data and land cells are set to No Data. |
| | 3211 | |
| | 3212 | If False, the template raster will only be used to define the |
| | 3213 | coordinate system, cell size, and rectangular extent of the output |
| | 3214 | raster, and no masking will be done."""), |
| | 3215 | arcGISDisplayName=_(u'Use template raster as mask'), |
| | 3216 | arcGISCategory=_(u'Interpolation and masking options')) |