General Question: Exporting Data with JS API

1623
5
05-14-2019 08:41 AM
ToddFagin
Occasional Contributor II

I have been searching ArcGIS for Developers and various forums to answer this question, but haven't found anything. That doesn't necessarily mean it isn't out there somewhere, I just haven't found it.

I anticipate a time when it would be nice to allow individuals to export data from a web app to shapefile, geodatabase, GeoJSON, or other formats. For instance, in both Portal and ArcGIS Online, I have an option to export a hosted feature layer to a variety of formats. Is there a widget (or even some sample code floating around out there) so that this functionality can be implemented within a JS API app?

Thanks,

Todd

Tags (3)
0 Kudos
5 Replies
UndralBatsukh
Esri Regular Contributor

Hi there, 

No we do not have something out of the box. You will probably have to write your own.

The following utility converts ArcGIS JSON geometries to GeoJSON geometries and vice versa.

https://github.com/Esri/arcgis-to-geojson-utils

Hope this helps

0 Kudos
ToddFagin
Occasional Contributor II

Of course, I was hoping to hear otherwise, but this is nonetheless useful information.

Thank you.

0 Kudos
JeffK
by MVP Regular Contributor
MVP Regular Contributor

Todd,

Have you seen the 'click and ship' or Extract Data example? It requires a geoprocessing service be set up. We just set one up to return a zip file of intersecting features.

Clip and Ship

Extract Data JS (3.28)

ToddFagin
Occasional Contributor II

This is great. I will look into this further and report back on my success or failure.

0 Kudos
JeffK
by MVP Regular Contributor
MVP Regular Contributor

Glad it helped. I thought I'd share how we set it up in our app since the examples I have found are imo dated and somewhat hard to follow.  So here it is, a process that takes input from the application and returns a zip file of all features that it intersects with.  We kept is simple, with one input parameter and the output.

The function is fired by a button click:

<button class="btn btn-primary" on:click="getZip">Get Zip File</button>

The function:

 getSiteZip () {
            fileService.selected(this.$http, this.fSearch.fileSearchId).then(response => {
                if (response.data && response.data.length > 0) {
                    var responseString = JSON.stringify(response.data)
                    ZipService.gpSiteZip(responseString)
                }
            })
        }‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

That calls to a function in another .js file passing it the results as a JSON string.  The zipdownload function initiates the download as soon as the zip file is ready.

function gpSiteZip (results) {
    return new Promise((resolve, reject) => {
            gp = new Geoprocessor(zipServiceUrl)
            var params = {
                Input_JSON_String: results
            }
            gp.submitJob(params, gpJobComplete, gpJobStatus, gpJobFailed).then(response => {
                resolve(response)
            })
        })
    })
}

function gpJobStatus (jobinfo) {
    var jobstatus = ''
    switch (jobinfo.jobStatus) {
    case 'esriJobSubmitted':
        jobstatus = 'Submitted...'
        break
    case 'esriJobExecuting':
        jobstatus = 'Executing...'
        break
    case 'esriJobSucceeded':
        jobstatus = 'Succedded...'
        break
    }
    console.log(jobstatus)
}

function gpJobComplete (jobinfo) {
    //get the Zipfile back from the service
    //console.log('job complete: ', jobinfo)
    gp.getResultData(jobinfo.jobId, 'GPDataFile', zipDownload)
}

function gpJobFailed (error) {
    console.log(error)
}

function zipDownload (zipResult) {
    var link = document.createElement('a')
    link.href = zipResult.value.url
    link.download = 'Download Zip'
    document.body.appendChild(link)
    link.click()
}

export default {
    gpSiteZip: gpSiteZip
}‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

The service is a script tool that returns a GPDataFile.  

The script's structure for creating the zip file looks like below- I took out the variables since I bet everyone who comes across this will have a different process that they can easily script. This is just to show an alternative to the ClipAndShip toolbox example.

# ------------------------------------------------------------------------------
def jsontosql(jsonstr):
    # Function to parse JSON string into TRS SQL query
    try:

        return sqlstring

    except (Exception, RuntimeError) as err:
        return err

# ------------------------------------------------------------------------------
def generateAOI(sqlstring, srcFC):
    # Function to generate the aoi from the sqlstring
    try:
        
        arcpy.MakeFeatureLayer_management()

        arcpy.SelectLayerByAttribute_management()

        aoi = arcpy.CreateUniqueName()

        arcpy.CopyFeatures_management()

        return aoi

    except (Exception, RuntimeError) as err:
        return err

# ------------------------------------------------------------------------------
def toCopy(aoi, fc_list):
    # Use the aoi to select the intersecting sites
    try:
        featureSelected = []

        for fc in fc_list:
            fcname = os.path.basename(fc).split('.')[-1].replace(' ', '')

            arcpy.MakeFeatureLayer_management()

            arcpy.SelectLayerByLocation_management()

            outfc = arcpy.CreateUniqueName()
           
            arcpy.CopyFeatures_management()
            
            featureInvSelected.append()

            arcpy.Delete_management()

        return featureSelected

    except (Exception, RuntimeError) as err:
        return err

# ------------------------------------------------------------------------------
def zipFolder(outFolder):
    try:
        # zip and return the folder
        shutil.make_archive(outFolder, 'zip', outFolder)
        outzip = '{}.zip'.format(outFolder)

        return outzip

    except (Exception, RuntimeError) as err:
        return err

# ------------------------------------------------------------------------------
def getSites(instring, outputDirectory, srcFC, fc_list):
    try:
        # set instance specific variables
        date = ''.join(datetime.now().isoformat().split('T')[0].split('-'))
        outputName = 'ExtractedFeatures_' + date

        # Parse instring to SQL
        sqlstring = jsontosql(instring)

        # generate aoi from the sqlstring
        aoi = generateAOI(sqlstring, srcFC)

        # generate selected sites from sql query
        Selsites = toCopy(aoi, fc_list)

        # Create the temporary folder with a unique name
        outFolder = arcpy.CreateUniqueName(outputName, outputDirectory)
        arcpy.CreateFolder_management(outputDirectory, os.path.basename(outFolder))

        # Iterate over the list of selected sites and copy them to the in_memory space
        for fc in Selsites:
            fcount = int(arcpy.GetCount_management(fc)[0])
            outfc = os.path.join(outFolder, os.path.basename(fc) + '.shp')
            if fcount > 0:
                arcpy.AddMessage('\t' + outfc)
                arcpy.CopyFeatures_management(fc, outfc)

        # Zip the folder
        outzip = zipFolder(outFolder)

        # Delete intermediate outputs
        cleanup(aoi, siteInvSelected)

        return outzip

    except (Exception, RuntimeError) as err:
        return err

# ------------------------------------------------------------------------------
if __name__ == '__main__':
    # Scratch directory created for each jobID
    outputDirectory = arcpy.env.scratchFolder

    # Input parameter for selecting the sections
    instring = arcpy.GetParameterAsText(0)

    if instring == '#' or not instring:
        # Input json object as string passed to service
        instring = '[Example of the input parameter here to assist in running the tool to get a result for publishing'
    
    # layers that will be extracted
    featurelayer1 = r''
    featurelayer2 = r''
    featurelayer3 = r''
    
    # featureclass used to query and select features from the other layers
    featurelayer6 = r''

    fc_list = [featurelayer1, featurelayer2, featurelayer3]

    # Parse input JSON object string to python native list of dictionaries
    instring = json.loads(instring)

    # Controls the processing and returns the zipped output folder of the intersecting sites provides the output 
    arcpy.SetParameter(1, getSites(instring, outputDirectory, srcFC, fc_list))
    ‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

Hope this helps with the process.

0 Kudos