ArcGIS SVR GP Service: How to deliver output file to user?

2297
5
Jump to solution
04-29-2016 01:35 PM
ThomasColson
MVP Frequent Contributor

I'm sure this is a really elementary solution, I'm just over thinking it. I have this really simple model that takes a feature class and converts it to a GPX.

Ran the model

Then published it successfully after registering the folder where the PY code is.

When I run the GP tool from the GIS Server connection in Arc Cat, I can see that the output GPX file is going to the jobs directory, but how can I get that file? The whole point of this is to have a simple-click-one-button tool so users can get a GPX file back on their hard drive.

try:
    from xml.etree import cElementTree as ET
except:
    from xml.etree import ElementTree as ET
import arcpy, sys, traceback
from arcpy import env
import time, os
from subprocess import call
unicode = str


OutName = arcpy.GetParameterAsText(0)




fms = arcpy.FieldMappings()
fms.addTable("\\\\some_server\DATASTORE\GP\\PLACE FISH.sde\\FISH.DBO.FISH_BARRIERS")
FLD_STATION_NAME = arcpy.FieldMap()
FLD_STREAMNAME = arcpy.FieldMap()
FLD_EDITDATE = arcpy.FieldMap()


FLD_STATION_NAME.addInputField("\\\\some_server\DATASTORE\GP\\PLACE FISH.sde\\FISH.DBO.FISH_BARRIERS", "STATION_NAME")  
STATION_NAME_MAP = FLD_STATION_NAME.outputField  
STATION_NAME_MAP.name = "Name"  
FLD_STATION_NAME.outputField = STATION_NAME_MAP  
fms.addFieldMap(FLD_STATION_NAME)


FLD_STREAMNAME.addInputField("\\\\some_server\DATASTORE\GP\\PLACE FISH.sde\\FISH.DBO.FISH_BARRIERS", "STREAMNAME")  
STREAMNAME_MAP = FLD_STREAMNAME.outputField  
STREAMNAME_MAP.name = "Descript"  
FLD_STREAMNAME.outputField = STREAMNAME_MAP  
fms.addFieldMap(FLD_STREAMNAME)


FLD_EDITDATE.addInputField("\\\\some_server\DATASTORE\GP\\PLACE FISH.sde\\FISH.DBO.FISH_BARRIERS", "EDITDATE")  
EDITDATE_MAP = FLD_EDITDATE.outputField  
EDITDATE_MAP.name = "DateTimeS"
EDITDATE_MAP.type = "Text" 
FLD_EDITDATE.outputField = EDITDATE_MAP  
fms.addFieldMap(FLD_EDITDATE)


arcpy.FeatureClassToFeatureClass_conversion("\\\\some_server\DATASTORE\GP\\PLACE FISH.sde\\FISH.DBO.FISH_BARRIERS", "\\\\some_server\DATASTORE\GP\\", "FISH_BARRIERS.shp", "", fms)


gpx = ET.Element("gpx", xmlns="http://www.topografix.com/GPX/1/1",
                 xalan="http://xml.apache.org/xalan",
                 xsi="http://www.w3.org/2001/XMLSchema-instance",
                 creator="Esri",
                 version="1.1")




def prettify(elem):
    """Return a pretty-printed XML string for the Element.
    """
    from xml.dom import minidom
    rough_string = ET.tostring(elem, 'utf-8')
    reparsed = minidom.parseString(rough_string)
    return reparsed.toprettyxml(indent="  ")






def featuresToGPX(inputFC, outGPX, zerodate, pretty):
    ''' This is called by the __main__ if run from a tool or at the command line
    '''


    descInput = arcpy.Describe(inputFC)
    if descInput.spatialReference.factoryCode != 4326:
        arcpy.AddWarning("Input data is not projected in WGS84,"
                         " features were reprojected on the fly to create the GPX.")


    generatePointsFromFeatures(inputFC , descInput, zerodate)


    # Write the output GPX file
    try:
        if pretty:
            gpxFile = open(outGPX, "w")
            gpxFile.write(prettify(gpx))
        else:
            gpxFile = open(outGPX, "wb")
            ET.ElementTree(gpx).write(gpxFile, encoding="UTF-8", xml_declaration=True)
    except TypeError as e:
        arcpy.AddError("Error serializing GPX into the file.")
    finally:
        gpxFile.close()






def generatePointsFromFeatures(inputFC, descInput, zerodate=False):


    def attHelper(row):
        # helper function to get/set field attributes for output gpx file


        pnt = row[1].getPart()
        valuesDict["PNTX"] = str(pnt.X)
        valuesDict["PNTY"] = str(pnt.Y)


        Z = pnt.Z if descInput.hasZ else None
        if Z or ("ELEVATION" in cursorFields):
            valuesDict["ELEVATION"] = str(Z) if Z else str(row[fieldNameDict["ELEVATION"]])
        else:
            valuesDict["ELEVATION"] = str(0)


        valuesDict["NAME"] = row[fieldNameDict["NAME"]] if "NAME" in fields else " "
        valuesDict["DESCRIPT"] = row[fieldNameDict["DESCRIPT"]] if "DESCRIPT" in fields else " "




        if "DATETIMES" in fields:
            row_time = row[fieldNameDict["DATETIMES"]]
            formatted_time = row_time if row_time else " "
        elif zerodate and "DATETIMES" not in fields:
            formatted_time = time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime(0))
        else:
            formatted_time = time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime(0)) if zerodate else " "


        valuesDict["DATETIMES"] = formatted_time


        return
    #-------------end helper function-----------------




    def getValuesFromFC(inputFC, cursorFields ):


        previousPartNum = 0
        startTrack = True


        # Loop through all features and parts
        with arcpy.da.SearchCursor(inputFC, cursorFields, spatial_reference="4326", explode_to_points=True) as searchCur:
            for row in searchCur:
                if descInput.shapeType == "Polyline":
                    for part in row:
                        newPart = False
                        if not row[0] == previousPartNum or startTrack is True:
                            startTrack = False
                            newPart = True
                        previousPartNum = row[0]


                        attHelper(row)
                        yield "trk", newPart


                elif descInput.shapeType == "Multipoint" or descInput.shapeType == "Point":
                    # check to see if data was original GPX with "Type" of "TRKPT" or "WPT"
                    trkType = row[fieldNameDict["TYPE"]].upper() if "TYPE" in fields else None


                    attHelper(row)


                    if trkType == "TRKPT":
                        newPart = False
                        if previousPartNum == 0:
                            newPart = True
                            previousPartNum = 1


                        yield "trk", newPart


                    else:
                        yield "wpt", None


    # ---------end get values function-------------




    # Get list of available fields
    fields = [f.name.upper() for f in arcpy.ListFields(inputFC)]
    valuesDict = {"ELEVATION": 0, "NAME": "", "DESCRIPT": "", "DATETIMES": "", "TYPE": "", "PNTX": 0, "PNTY": 0}
    fieldNameDict = {"ELEVATION": 0, "NAME": 1, "DESCRIPT": 2, "DATETIMES": 3, "TYPE": 4, "PNTX": 5, "PNTY": 6}


    cursorFields = ["OID@", "SHAPE@"]


    for key, item in valuesDict.items():
        if key in fields:
            fieldNameDict[key] = len(cursorFields)  # assign current index
            cursorFields.append(key)   # build up list of fields for cursor
        else:
            fieldNameDict[key] = None


    for index, gpxValues in enumerate(getValuesFromFC(inputFC, cursorFields)):


        if gpxValues[0] == "wpt":
            wpt = ET.SubElement(gpx, 'wpt', {'lon':valuesDict["PNTX"], 'lat':valuesDict["PNTY"]})
            wptEle = ET.SubElement(wpt, "ele")
            wptEle.text = valuesDict["ELEVATION"]
            wptTime = ET.SubElement(wpt, "time")
            wptTime.text = valuesDict["DATETIMES"]
            wptName = ET.SubElement(wpt, "name")
            wptName.text = valuesDict["NAME"]
            wptDesc = ET.SubElement(wpt, "desc")
            wptDesc.text = valuesDict["DESCRIPT"]


        else:  #TRKS
            if gpxValues[1]:
                # Elements for the start of a new track
                trk = ET.SubElement(gpx, "trk")
                trkName = ET.SubElement(trk, "name")
                trkName.text = valuesDict["NAME"]
                trkDesc = ET.SubElement(trk, "desc")
                trkDesc.text = valuesDict["DESCRIPT"]
                trkSeg = ET.SubElement(trk, "trkseg")


            trkPt = ET.SubElement(trkSeg, "trkpt", {'lon':valuesDict["PNTX"], 'lat':valuesDict["PNTY"]})
            trkPtEle = ET.SubElement(trkPt, "ele")
            trkPtEle.text = valuesDict["ELEVATION"]
            trkPtTime = ET.SubElement(trkPt, "time")
            trkPtTime.text = valuesDict["DATETIMES"]






if __name__ == "__main__":
    ''' Gather tool inputs and pass them to featuresToGPX
    '''


    inputFC = "\\\\some_server\DATASTORE\GP\\FISH_BARRIERS.shp"
    outGPX = OutName
    zerodate = "#"
    pretty = "#"
    featuresToGPX(inputFC, outGPX, zerodate, pretty)
    arcpy.Delete_management("\\\\some_server\DATASTORE\GP\\FISH_BARRIERS.shp")
0 Kudos
1 Solution

Accepted Solutions
DougBrowning
MVP Notable Contributor

This worked for me when using data drive pages.

output = targetJobname + ".pdf"

Output_File = os.path.join(arcpy.env.scratchFolder, output)

ddp.exportToPDF(Output_File, "CURRENT")

#forgot to add

arcpy.SetParameterAsText(1, Output_File)

View solution in original post

5 Replies
DougBrowning
MVP Notable Contributor

This worked for me when using data drive pages.

output = targetJobname + ".pdf"

Output_File = os.path.join(arcpy.env.scratchFolder, output)

ddp.exportToPDF(Output_File, "CURRENT")

#forgot to add

arcpy.SetParameterAsText(1, Output_File)

ThomasColson
MVP Frequent Contributor

Thanks. Based on https://community.esri.com/thread/104530#comment-388705 , Custom Geoprocessing Tool Reporting for ArcGIS online , Geoprocessing Service Output File scratch directory path to url , and Geoprocessing Service Output Directory  that seems to be the path. But I'm having a hard time figuring out how to a) replicate your example in my specific situation, and b) how to present the file to the user. Where this is all leading to, I'm hoping to embed the GP service in the GP Widget in WAB, with similar functionality as how Tutorial: Publishing additional services for printing—Documentation | ArcGIS for Server works in WAB: delivering a click-to-download pdf. In this case, I need a click-to-download gpx file. But first I have to figure out how to make the underlying logic work. Thanks!

0 Kudos
ThomasColson
MVP Frequent Contributor

So I've modified the PY as such:

    inputFC = "\\\\server\DATASTORE\GP\\FISH_BARRIERS.shp"

    outGPX = os.path.join(arcpy.env.scratchFolder, OutName)

    zerodate = "#"

    pretty = "#"

    featuresToGPX(inputFC, outGPX, zerodate, pretty)

    arcpy.Delete_management("\\\\server\DATASTORE\GP\\FISH_BARRIERS.shp")

Re-ran the tool, republished it, same result:

Added it to a WAB (developer) GP Widget

Hit execute, same result: nothing.

Am I missing something elementary-obvious or does this workflow only work with the export-web-map-to-pdf task example?

0 Kudos
ThomasColson
MVP Frequent Contributor

Update...implementing some of your answer, setting the job to asych, and setting "add to display" in the model solved this. You can infer I spent ALL WEEKEND ON THIS.....

Once I polish up the workflow I'll document it in a blog.

DougBrowning
MVP Notable Contributor

Opps looks like I missed this last line in my copy and paste which may do it.  Really sorry about that.

arcpy.SetParameterAsText(1, Output_File)