JSON Formatting for esri_json or geo_json

3834
8
Jump to solution
09-15-2015 11:09 AM
JamesCrandall
MVP Frequent Contributor

First...  May I please mention how awful it is working with JSON strings?  This is horrendous to have to wade through this string manipulation work.

Okay.  Can someone please step in here and provide a fresh set of eyes because mine are entirely crossed attempting to untangle this mess.  I suppose this has to do with the fact that the calling application is not necessarily "GIS" based and I am having to work with a non-GEO/ESRI JSON formatted string as an input then attempt to make sense out of it.

Anyway...

I need to take the input JSON and setup a FeatureCollection so that I can process individual polygons.

The input:

input ="""[
  {
    "rings" : 
    [
      [[-117,34],[-116,34],[-117,33],[-117,34]], [[-115,44],[-114,43],[-115,43],[-115,44]]
    ]
  },
  {
    "rings" : 
    [
      [[32,17],[31,17],[30,17],[30,16],[32,17]]
    ]
  }
]"""

I need it to read as such:

main_json_string = """{"type":"FeatureCollection","features":[
                      {"type":"Feature","geometry":{"type":"Polygon","coordinates":[
                                                                                    [[-117,34],[-116,34],[-117,33],[-117,34]]]
                                                   }
                      },
                      {"type":"Feature","geometry":{"type":"Polygon","coordinates":[
                                                                                    [[32,17],[31,17],[30,17],[30,16],[32,17]]]
                                                   }
                      }]}"""

I am unable to pick out from the squiglies how to get it correct.  I started with some replace methods and finding it difficult to get it all converted correctly.  Any help is appreciated.

0 Kudos
1 Solution

Accepted Solutions
JoshuaBixby
MVP Esteemed Contributor

I find manipulating JSON strings as strings to be challenging.  I recommend using the Python JSON library to convert JSON to Python and back again.

See if the following works for you:

def geo_convert(ring_string):
    from json import loads, dumps
    
    rings = loads(ring_string)
    feat_coll = {'type': 'FeatureCollection',
                'features':[]}
    
    for ring in rings:
        feat_coll['features'].append(
            {'type': 'Feature',
            'geometry': {
                'type': 'Polygon',
                'coordinates': [ring['rings'][0]]
            }})
    
    return dumps(feat_coll)

Pass your input to the function to get main_json_string back.

A couple of comments:

  • It appeared from your example that you only wanted the first/outer ring to create a polygon, so the function only grabs the first ring.
  • I am not sure how the nested lists for the coordinates are handled down stream. You may need to add an additional nesting list on line 13.

UPDATE:  Changed line #13 to include correction from James Crandall​.

View solution in original post

8 Replies
JoshuaBixby
MVP Esteemed Contributor

I find manipulating JSON strings as strings to be challenging.  I recommend using the Python JSON library to convert JSON to Python and back again.

See if the following works for you:

def geo_convert(ring_string):
    from json import loads, dumps
    
    rings = loads(ring_string)
    feat_coll = {'type': 'FeatureCollection',
                'features':[]}
    
    for ring in rings:
        feat_coll['features'].append(
            {'type': 'Feature',
            'geometry': {
                'type': 'Polygon',
                'coordinates': [ring['rings'][0]]
            }})
    
    return dumps(feat_coll)

Pass your input to the function to get main_json_string back.

A couple of comments:

  • It appeared from your example that you only wanted the first/outer ring to create a polygon, so the function only grabs the first ring.
  • I am not sure how the nested lists for the coordinates are handled down stream. You may need to add an additional nesting list on line 13.

UPDATE:  Changed line #13 to include correction from James Crandall​.

BruceHarold
Esri Regular Contributor

Hi

The AsShape function can take GeoJSON or EsriJSON to geometry.

Geometry objects have a __geo_interface__ attribute, which is GeoJSON.

Geometry objects have a JSON method, which is EsriJSON.

Like:

geom

<Polyline object at 0x3bba81d0[0x225fbad0]>

geom.JSON

'{"paths":[[[2692041.740699999,-18926.525499999523],[2692042.5719999969,-18916.945300001651],[2692034.5209999979,-18810.606699999422],[2692022.3840999976,-18663.190900001675],[2692007.4103000015,-18499.441799998283]]],"spatialReference":{"wkid":102242,"latestWkid":2767}}'

geom.__geo_interface__

{'type': 'MultiLineString', 'coordinates': [[(2692041.740699999, -18926.525499999523), (2692042.571999997, -18916.94530000165), (2692034.520999998, -18810.606699999422), (2692022.3840999976, -18663.190900001675), (2692007.4103000015, -18499.441799998283)]]}

0 Kudos
JoshuaBixby
MVP Esteemed Contributor

That was my initial thought, but the OP's JSON string doesn't work directly with arcpy.AsShape, it generates an AssertionError.  Since the original string needs to be manipulated, I figured just go straight to using the native JSON library to restructure the input.

0 Kudos
JamesCrandall
MVP Frequent Contributor

That's correct.  I have to add the necessary attributes to make it geo_JSON "ready"!

0 Kudos
JamesCrandall
MVP Frequent Contributor

Hi Bruce,

Yes that is exactly what I am doing!  However the JSON I am getting from the client/application is not formatted for GEO/ESRI JSON and this is what my OP is about: getting it into those formats.

0 Kudos
JamesCrandall
MVP Frequent Contributor

I appreciate the example Joshua!  I'll see about implementing it asap.

0 Kudos
JamesCrandall
MVP Frequent Contributor

Joshua,


Thanks again for that geo_convert def() --- very helpful!  I'm marking your reply as the answer with the updates below.

One minor syntax thingie that caused it some heartburn was a missing set of "[]" around the "ring" attribute.  Seems to work if I wrap 'coordinates': values with them as below:

def geo_convert(ring_string): 
    from json import loads, dumps 
   
    rings = loads(ring_string) 
    feat_coll = {'type': 'FeatureCollection', 
                'features':[]} 
   
    for ring in rings: 
        feat_coll['features'].append( 
            {'type': 'Feature', 
            'geometry': { 
                'type': 'Polygon', 
                'coordinates': [ring['rings'][0]]
            }}) 
   
    return dumps(feat_coll)
JoshuaBixby
MVP Esteemed Contributor

I was wondering about that extra nested list.  I will update my original code with your modification so the correct answer works correctly.