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

2208
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 Frequent 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 Frequent 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

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 Frequent 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)