Select to view content in your preferred language

Keys in dictionaries: the geojson example

80
0
Monday
Labels (1)
DanPatterson
MVP Esteemed Contributor
6 0 80

Geojson data.  I must admit, it is one of the weirdest standard data formats that I have come across.  But, they are out there and you adapt.

Begin with getting the keys of a nested dictionary.

def get_keys(data, num):
    """Return dictiony keys by level.

    Parameters
    ----------
    data : dictionary in geojson format
    num : beginning index number

    Useage
    ------
    r = get_keys(data, 0)
    """
    keys = []
    if isinstance(data, dict):
        num += 1
        for key, value in data.items():
            t = type(value).__name__
            keys.append((num, key, t))
            keys += get_keys(value, num)
    elif isinstance(data, list):
        for value in data[:1]:
            keys += get_keys(value, num)
            # num += 1
    return keys

An example of a simple geojson with 3 shapes, a couple of them multipolygons and some attributes (which I don't actually carry about, but they were there).

In this example, I returned the level, name and data type.  A quick modification to the above and you could return the values/items.

get_keys(vals, 0)
[(1, 'type', 'str'),
 (1, 'crs', 'dict'),
 (2, 'type', 'str'),
 (2, 'properties', 'dict'),
 (3, 'name', 'str'),
 (1, 'features', 'list'),
 (2, 'type', 'str'),
 (2, 'id', 'int'),
 (2, 'geometry', 'dict'),
 (3, 'type', 'str'),
 (3, 'coordinates', 'list'),
 (2, 'properties', 'dict'),
 (3, 'OBJECTID', 'int'),
 (3, 'Parts', 'int'),
 (3, 'Points', 'int'),
 (3, 'Curves', 'int'),
 (3, 'A0', 'str'),
 (3, 'A1', 'str'),
 (3, 'Longs_', 'int'),
 (3, 'Area_perim', 'float'),
 (3, 'srt_a0a1', 'int'),
 (3, 'Int_nulls', 'int'),
 (3, 'Float_nulls', 'NoneType'),
 (3, 'Text_nulls', 'str'),
 (3, 'OID_', 'int'),
 (3, 'Dbls_', 'int'),
 (3, 'Shape_Length', 'float'),
 (3, 'Shape_Area', 'float')]

Not bad, but one of the 1's is out of the expected order, so on to the next incarnation...printing to show the nested levels of the keys.

prn_keys(vals, 0)
1     type           str       
1     crs            dict      
  2     type           str       
  2     properties     dict      
    3     name           str       
1     features       list      
  2     type           str       
  2     id             int       
  2     geometry       dict      
    3     type           str       
    3     coordinates    list      
  2     properties     dict      
    3     OBJECTID       int       
    3     Parts          int       
    3     Points         int       
    3     Curves         int       
    3     A0             str       
    3     A1             str       
    3     Longs_         int       
    3     Area_perim     float     
    3     srt_a0a1       int       
    3     Int_nulls      int       
    3     Float_nulls    NoneType  
    3     Text_nulls     str       
    3     OID_           int       
    3     Dbls_          int       
    3     Shape_Length   float     
    3     Shape_Area     float     

You can use numpy to pull out information from the key data

kys = get_keys(vals, 0)
dt = np.dtype([('Level', 'i4'), ('Key_name', 'U15'), ('Type', 'U10')])
val_arr = np.asarray(kys, dtype=dt)
#
# -- do a little query, pull out the results, and show as a list
q = val_arr['Level'] == 1  # query, get all level 1 keys
result = val_arr[q]
result.tolist()
##
[(1, 'type', 'str'),
 (1, 'crs', 'dict'),
 (1, 'features', 'list')]

If you want to load a geojson and mess with the data structure yourself, here is a script

def load_geojson(pth, full=True, just_geometry=False):
    """Load a geojson file and convert to a Geo Array.

    The geojson is from the ``Features to JSON`` tool listed in the references.

    Parameters
    ----------
    pth : file path
        Full file path to the geojson file.
    full : boolean
        True to return a formatted geojson file.
    geometry : boolean
        True returns just the geometry of the file.

    Returns
    -------
    data : dictionary
        The full geojson dictionary of the geometry and its attributes.  The
        result is a nested dictionary::

    >>> data
    ... {'type':                                  # first feature
    ...  'crs': {'type': 'name', 'properties': {'name': 'EPSG:2951'}},
    ...  'features':
    ...     [{'type': 'Feature',
    ...       'id': 1,
    ...       'geometry': {'type':  'MultiPolygon',
    ...                    'coordinates': snip},  # coordinate values
    ...       'properties': snip }},              # attribute values from table
    ... {'type':                                  # next feature
    ...  ... repeat}

    geometry : list
        A list of lists representing the features, their parts (for multipart
        features) and inner holes (for polygons).

    Notes
    -----
    Using the Features to JSON tool in ArcGIS PRO, the .json option was used.
    - Unchecked : The output will be created as Esri JSON (.json).
    - Checked   : The output will be created in the GeoJSON format (.geojson).

    References
    ----------
    `geojson specification in detail
    <https://geojson.org/>`_.

    `Wikipedia link
    <https://en.wikipedia.org/wiki/GeoJSON>`_.

    `Features to JSON
    <https://pro.arcgis.com/en/pro-app/tool-reference/conversion/
    features-to-json.htm>`_.

    `JSON to Features
    <https://pro.arcgis.com/en/pro-app/tool-reference/conversion/
    json-to-features.htm>`_.
    """
    # import json  # required if run outside
    with open(pth) as f:
        data = json.load(f)
        type_key = pth.split(".")[-1]
    keys = list(data.keys())
    if 'features' in keys:
        shapes = data['features']
        coord_key = ['rings', 'coordinates'][type_key == 'geojson']
        coords = [s['geometry'][coord_key] for s in shapes]  # 'rings'
    if full and just_geometry:
        print("\nReturning full geojson and just the geometry portion")
        print("as a list")
        return data, coords
    if full:
        return data
    if just_geometry:
        return coords

If you are interested in alternate geometry construct, see and example on my github site (geo arrays)

NumPy Geometry (npg geometry arrays) 

Enjoy

 

About the Author
Retired Geomatics Instructor (also DanPatterson_Retired). Currently working on geometry projects (various) as they relate to GIS and spatial analysis. I use NumPy, python and kin and interface with ArcGIS Pro.
Labels