Tuesday, February 26, 2013

ArcGIS Server and Spatial Analyst

In ArcGIS 10.0, you needed to worry about path length for raster operations if the raster path plus file name is over 154 characters.  Now at 10.1, the character length has been increased to 254.  This means for most raster operations you will not have to alter the TEMP variable for the ArcGIS account.

If you do run into issues where spatial analyst or 3D analyst tools fail on server, but work on desktop in your model, try changing the ArcGIS account's TEMP environmental parameter to a different folder.

A good example would be c:\serverwrksp.  You also should make sure that the ArcGIS account has read/write access to that folder.


To shorten or change the path that server uses, you need to modify the ArcGIS account's TEMP variable.  Server should then honor the new destination path.  You might have to either log in and log off or reboot the box to get the OS to honor the new variable.

Here is how you change the variable in Windows:
To view or change environment variables:
  1. Right-click My Computer, and then click Properties.
  2. Click the Advanced tab.
  3. Click Environment variables.
  4. Click one the following options, for either a user or a system variable:
    • Click New to add a new variable name and value.
    • Click an existing variable, and then click Edit to change its name or value.
    • Click an existing variable, and then click Delete to remove it.
Please support my idea of having a python GUI by voting it up here.

Enjoy

Friday, February 22, 2013

Creating a List with n number of entries (Python)

Normally when you create a list object in python, you cannot specify the number of items in the object by default unlike say an array object in C# or C++.  I recently ran into a situation where I was using the arcpy.da cursor objects for table that could have n number of fields.  I wanted the row to be initialized with a default value, so I used the * operator on the list to multiply a single value into a multi-value list.

Example:

# Get a list of fields that are not BLOB, OBJECTID, RASTER, GEOMETRY or GUID
fields = [field.name for field in arcpy.ListFields(service_table) \
                  if not(field.type.upper() in ['BLOB', 'OID', 'RASTER','GEOMETRY','GUID'])
# create a row list with default values
row = [""]* len(fields)

This will create a list object called row, where each value by default is an empty string ("").

Enjoy

Thursday, February 21, 2013

Query Feature Service By Object IDs (Python)

To query a feature service using object ids, you need to perform a POST.  For those who don't know the difference between a "GET" and "POST" here is a quick blurb based on the HTML specifications:
"POST" means that former means that form data is to be encoded (by a browser) into a URL while the "GET" means that the form data is to appear within a message body. 

Even simpler, "GET" is basically for just getting data where a "POST" may involve anything, like updating data, sending, or creating data.

Using python, you can perform both "GET" and "POST" methods, but since we need a "POST" to query by IDs, here is a simple example.  Please note that most feature services limit 1000 features being returned, so if you want to grab all of the features from a feature service, you'll have to perform multiple queries to get all the features back in JSON format.

Example:


import urllib2
import urllib
import urlparse
import httplib

def query_by_objectid(url, objectIDStart=0, objectIDEnd=1001):
    """ performs a POST operation where the query is called using
        the object id method.  If a feature service has more than
        1000 records, use this method to get a range of features
        from the feature service.

        Inputs:
           :url:  - string of feature service URL
           :objectIDStart: - integer of the start whole number
           :objectIDEnd: - end range whole number
        Returns:
           returns string JSON of query
    """
    url = url + '/query'
    start = int(objectIDStart)
    end = int(objectIDEnd)

    objectIDs = ",".join([str(x) for x in range(start, end)])
    headers = {"Content-type": "application/x-www-form-urlencoded",
               "Accept": "text/plain"}
    parameters = {'objectIds' : objectIDs,
                  'f' : 'json'}
    urlparams = urllib.urlencode(parameters)
    parts = urlparse.urlparse(url)
    h = httplib.HTTPConnection(parts.netloc)
    headers = {"Content-type": "application/x-www-form-urlencoded", "Accept": "text/plain"}
    h.request('POST', parts.path, urlparams, headers)
    r = h.getresponse()
    return r.read()

if __name__ == "__main__":
    url = 'http://sampleserver1.arcgisonline.com/ArcGIS/rest/services/TaxParcel/AssessorsLiveLayers/MapServer/1'
    print query_by_objectid(url=url,
                            objectIDStart=0,
                            objectIDEnd=5)

So we have a simple "POST" example. This sample should work with both ArcGIS Server 10.1 and ArcGIS Online feature services.

Please support my idea of having a python GUI by voting it up here.

Enjoy

Thursday, February 14, 2013

Creating an Map Document From Python (10.1)

Previous posts, I have discussed ways to create map service layers from scratch, and now I'm going to show you how to create a blank map document using the 'ConvertWebMapToMapDocument' tool in ArcToolbox.  The beauty of this tool is that it doesn't have to be run on server, but can be run from desktop.  It uses JSON based on the ExportWebMap specification.

A JSON map consists of the following:
{ 
   "mapOptions": {}, 
   "operationalLayers": [], 
   "baseMap": [], 
   "exportOptions": {}, 
   "layoutOptions": {} 
}

import json
from arcpy import mapping
mapService = {} 
jsonDump = json.dumps(mapService) 
result = mapping.ConvertWebMapToMapDocument(jsonDump)
mxd = result.mapDocument
mxd.saveACopy(r"c:\temp\blank.mxd")

Now you can do whatever you need to do with your map document.

Wednesday, February 13, 2013

wx GUI Article for ArcGIS

Here is an interesting article on how to create an interactive GUI for ArcGIS 10.1.

Basically what it does is it creates the form before ArcMap/Catalog finishes loading therefore the core software doesn't prevent the mainloop() to fail.  At least that's my take on it. 

If you think custom GUIs should be part of the next major release, I suggest you go to the Esri Idea site and vote up this idea.  It will need YOUR support to get into the next release.

Happy GUI designing!


Monday, February 4, 2013

Convert Table or Feature class to CSV (10.1)

Sometimes you need to export data from a feature class or table to a CSV file.  CSV stands for comma separated values.  Wikipedia defines a CSV file as such:

A comma-separated values (CSV) file stores tabular data (numbers and text) in plain-text form. Plain text means that the file is a sequence of characters, with no data that has to be interpreted instead, as binary numbers. A CSV file consists of any number of records, separated by line breaks of some kind; each record consists of fields, separated by some other character or string, most commonly a literal comma or tab. Usually, all records have an identical sequence of fields.

To create a CSV file at 10.1, you need to strip out certain field types: Geometry, Blob and Raster because the file format only supports plain text.  This can be done by doing the following:

 fieldnames = [f.name for f in desc.fields if f.type not in ["Geometry", "Raster", "Blob"]]

Next you need to write the field names and rows to a file.  This is extremely easy with the CSV library in python.  Documentation about this standard library can be found here.


def get_rows(data_set, fields):
   with da.SearchCursor(data_set, fields) as cursor:
      for row in cursor:
         yield row
if __name__ == "__main__":
   data_set = arcpy.GetParameterAsText(0) # feature class/Table
   output = arcpy.GetParameterAsText(1) # csv file
   desc = arcpy.Describe(data_set)
   fieldnames = [f.name for f in desc.fields if f.type not in ["Geometry", "Raster", "Blob"]]
   rows = get_rows(data_set, fieldnames)
   with open(output,'wb') as out_file:
      out_writer = csv.writer(out_file)
      out_writer.writerow(fieldnames)
      for row in rows:
         out_writer.writerow(row)

Here we have a function called get_rows() which takes two parameters.  The first is the data_set, which can be a table or feature class.  The next is the fields.  At 10.1, you must define your fields unlike the 10.0 cursor objects.  The function uses the yield, which is a generator.  Basically the code only runs if the function is called in a loop (I know that's not 100% correct), but here is a better explanation.  Using the CSV module in python, we can then easily write out each row within the rows generator object.

FYI, this is written so you can put this code into a script and toolbox for ArcGIS 10.1.  Just add the imports.

Enjoy