Where is the syntax error in this python toolbox?

589
2
Jump to solution
06-24-2021 12:58 PM
MattWilkie1
Occasional Contributor II

ArcGIS Pro tells me there is a syntax error when I run the single tool in this toolbox. Contrary to what the doc page for ERROR 00989 says it does not report what or where the error is. I can't find it visually and  `python -m compileall ImageRepo.pyt` says it's fine. Can any of you see what is wrong?

I'm using ArcGIS Pro v2.8.1.

The toolbox: https://gist.github.com/maphew/0bae6f3005ca129f2a9566e343cf37d7

MattWilkie1_0-1624564437194.png

GdalToCog
=====================
Parameters

Input Raster Finlayson124_SP6_13Sep2017_150cm_pro_nd.tif
Output Raster D:\work\ImgRepo\2021-06-10\pro_pyt_to_cog.tif
=====================
Messages

ERROR 000989: Python syntax error: within script T:\ENV.558\ImageRepo.pyt

 

Tags (3)
0 Kudos
1 Solution

Accepted Solutions
Tim_McGinnes
Occasional Contributor III

It is working fine for me in Pro 2.8.1 when I specify an input tif file stored on disk. I can make it error when choosing a tif file from my map, however the error is different (No such file or directory). I think GDAL cannot resolve the path when running it this way. In your parameters, the input file doesn't have a path - maybe try passing it the full path and filename. Does it error on other input files?

The other option is that something messed up when creating your local copy of the script or maybe some issue with your Python environment? Are you using the default environment?

This works:

# -*- coding: utf-8 -*-
import arcpy
from osgeo import gdal
gdal.UseExceptions()

class Toolbox(object):
    def __init__(self):
        """Define the toolbox (the name of the toolbox is the name of the
        .pyt file)."""
        self.label = "Toolbox"
        self.alias = "toolbox"

        # List of tool classes associated with this toolbox
        self.tools = [Tool]

class Tool(object):
    def __init__(self):
        """Gdal to Cloud Geotiff translator."""
        self.label = "GdalToCog"
        self.description = "Compress a raster to Cloud Optimized Geotiff, using the options that work best for our imagery at Environment Yukon"
        self.canRunInBackground = False

    def getParameterInfo(self):
        """Define parameter definitions"""
        params = []
        params += [arcpy.Parameter(
            displayName = "Input Raster",
            name = "in_raster",
            datatype="GPRasterLayer",
            parameterType="Required",
            direction="Input"
            )]
        params += [arcpy.Parameter(
            displayName="Output Raster",
            name="out_raster",
            datatype="DERasterDataset",
            parameterType="Required",
            direction="Output"
            )]
        return params

    def isLicensed(self):
        """Set whether tool is licensed to execute."""
        return True

    def updateParameters(self, parameters):
        """Modify the values and properties of parameters before internal
        validation is performed.  This method is called whenever a parameter
        has been changed."""
        return

    def updateMessages(self, parameters):
        """Modify the messages created by internal validation for each tool
        parameter.  This method is called after internal validation."""
        return

    def execute(self, parameters, messages):
        """Source code (adapted from 'gdal-to-cog.py')"""
        
        infile  = parameters[0].valueAsText
        outfile = parameters[1].valueAsText

        gdal.SetConfigOption('GDAL_CACHEMAX','30%')
        options = ["COMPRESS=ZSTD", "PREDICTOR=YES", "LEVEL=17", "BIGTIFF=YES",
            "NUM_THREADS=ALL_CPUS",]

        def progress_cb(complete, message, cb_data):
            '''Emit progress report in numbers for 10% intervals and dots for 3%'''
            if int(complete*100) % 10 == 0:
                print(f'{complete*100:.0f}', end='', flush=True)
            elif int(complete*100) % 3 == 0:
                print(f'{cb_data}', end='', flush=True)

        arcpy.AddMessage(f'Translating {infile} to {outfile}')
        gdal.Translate(outfile, infile, creationOptions=options, 
            format="COG",
            callback=progress_cb,
            callback_data='.'
            )
        return

 

View solution in original post

2 Replies
Tim_McGinnes
Occasional Contributor III

It is working fine for me in Pro 2.8.1 when I specify an input tif file stored on disk. I can make it error when choosing a tif file from my map, however the error is different (No such file or directory). I think GDAL cannot resolve the path when running it this way. In your parameters, the input file doesn't have a path - maybe try passing it the full path and filename. Does it error on other input files?

The other option is that something messed up when creating your local copy of the script or maybe some issue with your Python environment? Are you using the default environment?

This works:

# -*- coding: utf-8 -*-
import arcpy
from osgeo import gdal
gdal.UseExceptions()

class Toolbox(object):
    def __init__(self):
        """Define the toolbox (the name of the toolbox is the name of the
        .pyt file)."""
        self.label = "Toolbox"
        self.alias = "toolbox"

        # List of tool classes associated with this toolbox
        self.tools = [Tool]

class Tool(object):
    def __init__(self):
        """Gdal to Cloud Geotiff translator."""
        self.label = "GdalToCog"
        self.description = "Compress a raster to Cloud Optimized Geotiff, using the options that work best for our imagery at Environment Yukon"
        self.canRunInBackground = False

    def getParameterInfo(self):
        """Define parameter definitions"""
        params = []
        params += [arcpy.Parameter(
            displayName = "Input Raster",
            name = "in_raster",
            datatype="GPRasterLayer",
            parameterType="Required",
            direction="Input"
            )]
        params += [arcpy.Parameter(
            displayName="Output Raster",
            name="out_raster",
            datatype="DERasterDataset",
            parameterType="Required",
            direction="Output"
            )]
        return params

    def isLicensed(self):
        """Set whether tool is licensed to execute."""
        return True

    def updateParameters(self, parameters):
        """Modify the values and properties of parameters before internal
        validation is performed.  This method is called whenever a parameter
        has been changed."""
        return

    def updateMessages(self, parameters):
        """Modify the messages created by internal validation for each tool
        parameter.  This method is called after internal validation."""
        return

    def execute(self, parameters, messages):
        """Source code (adapted from 'gdal-to-cog.py')"""
        
        infile  = parameters[0].valueAsText
        outfile = parameters[1].valueAsText

        gdal.SetConfigOption('GDAL_CACHEMAX','30%')
        options = ["COMPRESS=ZSTD", "PREDICTOR=YES", "LEVEL=17", "BIGTIFF=YES",
            "NUM_THREADS=ALL_CPUS",]

        def progress_cb(complete, message, cb_data):
            '''Emit progress report in numbers for 10% intervals and dots for 3%'''
            if int(complete*100) % 10 == 0:
                print(f'{complete*100:.0f}', end='', flush=True)
            elif int(complete*100) % 3 == 0:
                print(f'{cb_data}', end='', flush=True)

        arcpy.AddMessage(f'Translating {infile} to {outfile}')
        gdal.Translate(outfile, infile, creationOptions=options, 
            format="COG",
            callback=progress_cb,
            callback_data='.'
            )
        return

 

MattWilkie1
Occasional Contributor II

Interesting. On my machine I needed to a) use a full path and not a map layer, and b) disable the `callback` options. I tried with both default Pro python and a clone env (created using File >> Python >> Manage).

I think there might be something screwy with my install though, because a test python toolbox with nothing in it but the demo Set Progressor takes over 4 minutes to run, but the actual tool execution part is only 18 seconds.

0 Kudos