Take the 2-minute tour ×
Geographic Information Systems Stack Exchange is a question and answer site for cartographers, geographers and GIS professionals. It's 100% free, no registration required.

The AsShape documentation provides examples that work very well.

However, I have been given what I believe to be valid GeoJSON that is a FeatureCollection of two polygons each with a Label.

It validates fine with the JSON Formatter & Validator but when I try AsShape on it, as per the code below, it throws an AssertionError on FeatureCollection.

I'm assuming that is because AsShape does not support FeatureCollections and so am wondering if there is another approach that I could use?

import arcpy

polyX2_string = {"type":"FeatureCollection","features":[{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[151.669111722222,-28.8913181547568],[151.669712422222,-28.890733765868],[151.670252222222,-28.8896441436458],[151.670847433333,-28.8891966436458],[151.670976977778,-28.8865389769791],[151.670112977778,-28.8846608880903]]]},"properties":{"Label":"National Park"}},{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[151.711278388889,-28.8201784658692],[151.710854355556,-28.8192543325359],[151.7100674,-28.8187800769803],[151.709037344444,-28.8184233992026],[151.707838755556,-28.8182234992026],[151.705821266667,-28.8180901992026],[151.705597933333,-28.8194629992025],[151.705198088889,-28.8212781880914]]]},"properties":{"Label":"National Park"}}]}

polygon = arcpy.AsShape(polyX2_string)
share|improve this question
    
Well right off the bat, it won't work because a shape can only have one feature, although that one feature can be made up of multiple parts. Additionally, it looks like a FeatureCollection can contain dissimilar types of features, while a shapefile or feature class can only contain one type of feature. So you will need some additional logic to decompose the JSON object into individual features as well as match only the feature type supported by your feature class. –  blah238 Mar 24 '12 at 1:48
    
Thanks blah238 - I'll work with the developer who is writing the GeoJSON that I ingest, to see if the polygon(s) can be passed as polygons rather than FeatureCollections. –  PolyGeo Mar 26 '12 at 11:20
add comment

4 Answers 4

up vote 3 down vote accepted

I have not used the AsShape before. Looking at the samples they all seems to start at the "type": portion of the json. So, you should be able to loop through the json after it is loaded and get to that part. It should be a python dict that you can then just go through the key, value pairs and pull what you need. The code below has been run but you will most likely need to modify. Once you get to the geometry you can start filtering by type. That should then let you process the various shapes within the feature collection. No need to split them first just adjust the dictionary loop and add in some Error checks. Bang!

import json, arcpy

main_json_string = '{"type":"FeatureCollection","features":[{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[151.669111722222,-28.8913181547568],[151.669712422222,-28.890733765868],[151.670252222222,-28.8896441436458],[151.670847433333,-28.8891966436458],[151.670976977778,-28.8865389769791],[151.670112977778,-28.8846608880903]]]},"properties":{"Label":"National Park"}},{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[151.711278388889,-28.8201784658692],[151.710854355556,-28.8192543325359],[151.7100674,-28.8187800769803],[151.709037344444,-28.8184233992026],[151.707838755556,-28.8182234992026],[151.705821266667,-28.8180901992026],[151.705597933333,-28.8194629992025],[151.705198088889,-28.8212781880914]]]},"properties":{"Label":"National Park"}}]}'


jsonData = json.loads(main_json_string)

for key,value in jsonData.iteritems():
    if value == 'FeatureCollection': # skip the salad and get to the steak
        pass
    else:
        for i in value:                    
            try:
                for k,v in i.iteritems():
                    if k == 'geometry':
                        print v
                        polygon = arcpy.AsShape(v)
                        print "Made it this far with no errors"
                    else:
                        pass
            except:
                pass
share|improve this answer
add comment

I'm going to give theJones the points for Answer and Jason an upvote because both were helpful. I'd actually worked out a solution soon after asking the question which I had been intending to post after further refinement - I will do so now.

import arcpy,json

jsonGeometry_string = '{"type":"FeatureCollection","features":[{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[151.669111722222,-28.8913181547568],[151.669712422222,-28.890733765868],[151.670252222222,-28.8896441436458],[151.670847433333,-28.8891966436458],[151.670976977778,-28.8865389769791],[151.670112977778,-28.8846608880903]]]},"properties":{"Label":"National Park"}},{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[151.711278388889,-28.8201784658692],[151.710854355556,-28.8192543325359],[151.7100674,-28.8187800769803],[151.709037344444,-28.8184233992026],[151.707838755556,-28.8182234992026],[151.705821266667,-28.8180901992026],[151.705597933333,-28.8194629992025],[151.705198088889,-28.8212781880914]]]},"properties":{"Label":"National Park"}}]}'
jsonGeometry_object = json.loads(jsonGeometry_string)

testFolder = "C:\\Temp"
testGDBName = "PolyFromJSONtest.gdb"
testGDB = testFolder + "\\" + testGDBName
if arcpy.Exists(testGDB):
    arcpy.Delete_management(testGDB)
arcpy.CreateFileGDB_management(testFolder, testGDBName)

prjFile = "C:/Program Files/ArcGIS/Desktop10.0/Coordinate Systems/Geographic Coordinate Systems/Australia and New Zealand/Geocentric Datum of Australia 1994.prj"
sr = arcpy.SpatialReference(prjFile)

arcpy.CreateFeatureclass_management(testGDB, "PolyFromJSON", "POLYGON", spatial_reference=sr)
arcpy.AddField_management(testGDB + "\\PolyFromJSON", "ParkName", "TEXT", field_length=50)
arcpy.CopyFeatures_management(testGDB + "\\PolyFromJSON",testGDB + "\NewPolyNoValue")

for jsonFeat in jsonGeometry_object['features']:
    polygon = arcpy.AsShape(jsonFeat['geometry'])
    label_string = jsonFeat['properties']['Label']
    print label_string
    arcpy.CopyFeatures_management(testGDB + "\NewPolyNoValue",testGDB + "\\NewPolyWithValue")
    arcpy.Append_management([polygon], testGDB + "\\NewPolyWithValue", "NO_TEST")
    arcpy.CalculateField_management(testGDB + "\\NewPolyWithValue", "ParkName", "'" + label_string + "'","PYTHON")
    arcpy.AddMessage("Appending " + label_string + " polygon into " + testGDB + "\\PolyFromJSON")
    arcpy.Append_management([testGDB + "\\NewPolyWithValue"], testGDB + "\\PolyFromJSON", "NO_TEST")
    arcpy.Delete_management(testGDB + "\\NewPolyWithValue")

arcpy.Delete_management(testGDB + "\NewPolyNoValue")
share|improve this answer
add comment

arcpy.AsShape only accepts pure GeoJSON geometries (Polygon, PolyLine, Point) as defined by this spec, not Feature records.

share|improve this answer
add comment

PolyGeo - I had a similar need to import geoJson and your script was a great starting point for me. The method of creating and deleting a temporary working featureset was too slow for my needs though, processing thousands of objects while the user waits. I found the InsertCursor method, which allows building and inserting rows into existing features. It also crudely tries to create all fields and identify basic types on the new feature class. My version:

import urllib2, arcpy, json

url = arcpy.GetParameterAsText(0)
featureClassName = arcpy.GetParameterAsText(1)
featureClassPath = arcpy.GetParameterAsText(2)

def testFloat(s):
    try:
        float(s)
        return True
    except ValueError:
        return False

def testInt(s):
    try:
        int(s)
        return True
    except ValueError:
        return False

testJson = '{ "type": "FeatureCollection", "features": [ { "type": "Feature", "geometry": { "type": "Point", "coordinates": [ -98.7445, 33.387001 ] }, "properties": { "SiteCode": "447910", "VariableCode": "MPE", "Value": "0.639999991282821" } }, { "type": "Feature", "geometry": { "type": "Point", "coordinates": [ -98.7068, 33.347801 ] }, "properties": { "SiteCode": "446210", "VariableCode": "MPE", "Value": "0.949999986216426" } } ] }'

response = urllib2.urlopen(url)
rawJson = response.read()
#IIS result has UTF-8 BOM
rawJson = rawJson.decode("utf-8-sig")
jsonData = json.loads(rawJson)

#esriJson settings
jsonFeatures = "features"
jsonFields = "attributes"
jsonGeom = "geometry"
#geojson settings
jsonFeatures = "features"
jsonFields = "properties"
jsonGeom = "geometry"

#prjFile = "C:/Program Files/ArcGIS/Desktop10.0/Coordinate Systems/Geographic Coordinate Systems/Australia and New Zealand/Geocentric Datum of Australia 1994.prj"
#sr = arcpy.SpatialReference(prjFile)
sr = arcpy.SpatialReference(4269)

featureFullName = featureClassPath + "/" + featureClassName

fields = []

if arcpy.Exists(featureFullName):
    arcpy.Delete_management(featureFullName)
#Final result Feature Class
arcpy.CreateFeatureclass_management(featureClassPath, featureClassName, "POINT", spatial_reference=sr)

#scan json for all fields
# this could be much faster with esriJson by using the jsonData['fields'] attributes. No such luck in geoJson 
for jsonFeat in jsonData[jsonFeatures]:    
    print jsonFeat
    # Check for fields to be added
    print jsonFeat[jsonFields]
    for key, val in jsonFeat[jsonFields].iteritems():
        print key
        if (key[:10] not in fields):
            fields.append(key[:10])
            if(testInt(val)):
                arcpy.AddField_management(featureFullName, key[:10], "LONG")
            elif (testFloat(val)):
                arcpy.AddField_management(featureFullName, key[:10], "FLOAT")
            else:
                arcpy.AddField_management(featureFullName, key[:10], "TEXT", field_length=50)


# Create insert cursor for table
rows = arcpy.InsertCursor(featureFullName)

#Now read data from json
for jsonFeat in jsonData[jsonFeatures]:
    polygon = arcpy.AsShape(jsonFeat[jsonGeom])

    #add the json geometry to row
    row = rows.newRow()
    row.shape = polygon

    # set all field values
    for key,val in jsonFeat[jsonFields].iteritems():
        row.setValue(key[:10], val)

    rows.insertRow(row)

# Delete cursor and row objects to remove locks on the data
del row
del rows
share|improve this answer
    
Thanks for posting this - my application only ever needs to ingest a handful of polygons (with thousands of vertices each) so I had not noticed that bottleneck on the code I put together. I'll use an InsertCursor rather than Append next time I encounter this requirement. –  PolyGeo May 1 at 21:21
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.