Select to view content in your preferred language

Keys in dictionaries: the geojson example

718
1
07-29-2024 06:16 PM
Labels (1)
DanPatterson
MVP Esteemed Contributor
6 1 718

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

 

1 Comment
Contributors
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