Select to view content in your preferred language

Convert JSON into FeatureCollection

7243
15
Jump to solution
03-30-2017 10:37 AM
JamesCrandall
MVP Frequent Contributor

I've been provided a JSON output in the sample below and tasked with getting this into a point feature class somehow.

[
 {
  "Name": "w1",
  "Long": "-60.44",
  "Lat": "21.60",
  "table": [
   [
    "G310-P:",
    "84 cfs; Mar 2, 17"
   ],
   [
    "G251-P:",
    "0 cfs; Mar 2, 17"
   ]
  ],
  "Heading": "315"
 },
 {
  "Name": "w2",
  "Long": "-60.32",
  "Lat": "21.63",
  "table": [
   [
    "S362-P:",
    "136 cfs; Mar 2, 17"
   ]
  ],
  "Heading": "0"
 },
 {
  "Name": "w2",
  "Long": "-60.35",
  "Lat": "21.37",
  "table": [
   [
    "S10A-C:",
    "0 cfs; Mar 2, 17"
   ],
   [
    "S10C-C:",
    "0 cfs; Mar 2, 17"
   ],
   [
    "S10D-C:",
    "0 cfs; Mar 2, 17"
   ]
  ],
  "Heading": "20"
 }
]‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

I have some existing processes that get close but hoped to run it by the community to see if someone spots the obvious solution before I dillydally down the wrong path for too long.  In the JSON sample above, I have Lat/Lon attributes that I would need to get into an array in order to use this def() to convert it to something closer to what I need.  My first thought is to create a new attribute called 'rings' so that it would just work with my geo_convert() def:

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': 'Point',  
                'coordinates': ring['rings'][0]
            }})  
    
    return dumps(feat_coll)  

Any ideas on what I could do? 

0 Kudos
15 Replies
RandyBurton
MVP Alum

 My suggestions.

# Third double quote in this line should be a colon
jData = json.loads('{ "features" " ' + fp.read().decode("utf-8-sig").encode("utf-8") + '}')

# Corrected
jData = json.loads('{ "features" : ' + fp.read().decode("utf-8-sig").encode("utf-8") + '}')

# An alternative, which may actually be better ....

# create an empty dictionary of features
jData = { "features" : None }

with open(filename, 'r') as fp:
    # read json and insert data into jData features dictionary
    jData["features"] = json.loads(fp.read())

# I also deleted the utf-8 decode/encode which may not be needed 
JamesCrandall
MVP Frequent Contributor

thank you!

0 Kudos
RandyBurton
MVP Alum

Glad to help.  And I learned a few things, too.

RandyBurton
MVP Alum

I noticed "Arrow Size".  If this will become a field name, don't forget to remove/replace the space.

RandyBurton
MVP Alum

My thoughts on your code is to replace lines 14 and 15

jData = json.dumps(output)    
json.dump(output, jsonfile, indent=2, sort_keys=True)

with this:

# create a dictionary of features
jData = { "features" : None }

# output is a list of dictionaries, in this case features
# insert the list into the jData dictionary
jData["features"] = output

# save the jData dictionary to a json file
json.dump(jData, jsonfile, indent=2, sort_keys=True)‍‍

You could combine the scripts, basically starting with the first 12 lines of your script (with the includes, etc.), then the six lines above, and finally including my script starting with line 12.  You would end up with something like this:

import csv
import json

csvfile = open('MyFile.csv', 'r')
reader = csv.DictReader(csvfile)
fieldnames = ('Name', 'Flow', 'Arrow Size', 'Date', 'Lat', 'Long', 'Heading', 'Color', 'Desciption', 'table')

output = []

for each in reader:
  row = {}
  for field in fieldnames:
    if not field == 'Coords':
        row[field] = each[field]    
  output.append(row)

jData = { "features" : None }
jData["features"] = output

# start the geojson file
geo = {
    "type" : "FeatureCollection",
    "crs" : { "type" : "name",
              "properties" : {
                  "name" : "EPSG:4326"
                  }
              },
    "features" : None
    }

feature_list = []

# loop through json
fCount = 0
for feature in jData['features']:
    fCount +=1
    features = {
        "type" : "Feature",
        "id" : fCount,
        "geometry" : { "type" : "Point",
                       "coordinates" : [
                           float(feature["Lat"]), float(feature["Long"]) ]
                       },
        "properties" : { "OBJECTID" : fCount,
                         "Name" : feature["Name"],
                         "Lat" : float(feature["Lat"]),
                         "Long" : float(feature["Long"]),
                         "Heading" : int(feature["Heading"])
                         }
        }
    
    feature_list.append(features)

    # this part would be exported to tab/csv file
    # for k, v in feature['table']:
    #   print str(fCount)+ '\t' + k + '\t' + v
        

geo["features"] = feature_list

# to write to a file
with open('result2.json', 'w') as fp:
     json.dump(geo, fp)
JamesCrandall
MVP Frequent Contributor

Good stuff.  I appreciate it and will try to implement something today!

0 Kudos