Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Converting JSON to QGIS GeoJSON: while having multiple features and different types

Currently I have a program that request JSONS from specific API. The creators of the API have claimed this data is in GeoJSON but QGIS cannot read it.

So I want to extend my Python Script to converting the JSON to GEOJSON in a readable format to get into QGIS and process it further there.

However, I have a few problems, one I do not know where to start and How the JSON files are constructed...its kind of mess. The JSONS were actually maded based on two API Get Request. One Api Get Request request the Points and the second one requests the details of the points. See this question here for some more context: API Request within another API request (Same API) in Python

Note because of character limit I cannot post the code here:

These details are of course supposed to be "the contours" of the area surrounding these points. The thing is..the point itself is mentioned in the JSON, making it kind of hard to specify what coordinate is for each point. Not to mention all other attributes that are intresting to us, are in the "point" part of the GeoJSON.

Take a look at the JSON itself to see what I mean:

{
    "comment": null,
    "contactDetails": {
        "city": null,
        "country": "BE",
        "email": null,
        "extraAddressInfo": null,
        "firstName": null,
        "kboNumber": null,
        "lastName": "TMVW",
        "number": null,
        "organisation": "TMVW - Brugge-Kust",
        "phoneNumber1": null,
        "phoneNumber2": null,
        "postalCode": null,
        "street": null
    },
    "contractor": null,
    "description": "WEGENISWERKEN - Oostende - 154-W - H Baelskaai-project Oosteroever",
    "diversions": [],
    "endDateTime": "2021-06-30T00:00:00",
    "gipodId": 1042078,
    "hindrance": {
        "description": null,
        "direction": null,
        "effects": [
            "Omleiding: beide richtingen",
            "Afgesloten: volledige rijweg",
            "Fietsers hebben geen doorgang",
            "Handelaars bereikbaar",
            "Plaatselijk verkeer: toegelaten",
            "Voetgangers hebben doorgang"
        ],
        "important": true,
        "locations": [
            "Voetpad",
            "Fietspad",
            "Parkeerstrook",
            "Rijbaan"
        ]
    },
    "latestUpdate": "2020-12-01T12:16:00.03",
    "location": {
        "cities": [
            "Oostende"
        ],
        "coordinate": {
            "coordinates": [
                2.931988215468502,
                51.23633810341717
            ],
            "crs": {
                "properties": {
                    "name": "urn:ogc:def:crs:OGC:1.3:CRS84"
                },
                "type": "name"
            },
            "type": "Point"
        },
        "geometry": {
            "coordinates": [
                [
                    [
                        2.932567101705748,
                        51.23657315009855
                    ],
                    [
                        2.9309934586397337,
                        51.235776874431004
                    ],
                    [
                        2.9328606392338914,
                        51.2345112414401
                    ],
                    [
                        2.9344086040607285,
                        51.23535468563417
                    ],
                    [
                        2.9344709862243095,
                        51.23529463700852
                    ],
                    [
                        2.932928489694045,
                        51.23447026126373
                    ],
                    [
                        2.935453674897618,
                        51.2326691257775
                    ],
                    [
                        2.937014893295095,
                        51.23347469462423
                    ],
                    [
                        2.9370649363167556,
                        51.23342209549579
                    ],
                    [
                        2.9355339718818847,
                        51.23261689467634
                    ],
                    [
                        2.937705787093551,
                        51.23108125372614
                    ],
                    [
                        2.939235922008332,
                        51.23191301940206
                    ],
                    [
                        2.9393162149112086,
                        51.231860784836144
                    ],
                    [
                        2.9377921292631313,
                        51.23102909334536
                    ],
                    [
                        2.9395494398210404,
                        51.22978103014327
                    ],
                    [
                        2.9395326861153492,
                        51.22973522407282
                    ],
                    [
                        2.9307116955342982,
                        51.23588365892173
                    ],
                    [
                        2.93077732400986,
                        51.235914858980586
                    ],
                    [
                        2.930921969180147,
                        51.23581685905391
                    ],
                    [
                        2.932475593354336,
                        51.23662429379119
                    ],
                    [
                        2.932567101705748,
                        51.23657315009855
                    ]
                ]
            ],
            "crs": {
                "properties": {
                    "name": "urn:ogc:def:crs:OGC:1.3:CRS84"
                },
                "type": "name"
            },
            "type": "Polygon"
        }
    },
    "mainContractor": null,
    "owner": "TMVW - Brugge-Kust",
    "reference": "DOM-154/15/004-W",
    "startDateTime": "2017-05-02T00:00:00",
    "state": "In uitvoering",
    "type": "Wegeniswerken (her)aanleg",
    "url": null
}

As a reminder these JSON's are not supposed to be point files and the geometry can be either a polygon as seen above: A multipolygon or a multiLinestring (do not have an example here).

So how do I get started and make sure I not only get the detail attributes out but clearly the contour geometry and coordinates?

The only Unique id is the GIPOD ID, because this one is where the actual link in this API database is.

Edit 1:

So this is the supposed geojson standard.

{
  "type": "Feature",
  "geometry": {
    "type": "Point",
    "coordinates": [125.6, 10.1]
  },
  "properties": {
    "name": "Dinagat Islands"
  }
}

Based on this standard, the converted JSON should be this:

    "type": "Feature",
               "geometry": {
                   "type": "Polygon",
                   "coordinates": [
                    [
                        [
                            2.932567101705748,
                            51.23657315009855
                        ],
                        [
                            2.9309934586397337,
                            51.235776874431004
                        ],
                        [
                            2.9328606392338914,
                            51.2345112414401
                        ],
                        [
                            2.9344086040607285,
                            51.23535468563417
                        ],
                        [
                            2.9344709862243095,
                            51.23529463700852
                        ],
                        [
                            2.932928489694045,
                            51.23447026126373
                        ],
                        [
                            2.935453674897618,
                            51.2326691257775
                        ],
                        [
                            2.937014893295095,
                            51.23347469462423
                        ],
                        [
                            2.9370649363167556,
                            51.23342209549579
                        ],
                        [
                            2.9355339718818847,
                            51.23261689467634
                        ],
                        [
                            2.937705787093551,
                            51.23108125372614
                        ],
                        [
                            2.939235922008332,
                            51.23191301940206
                        ],
                        [
                            2.9393162149112086,
                            51.231860784836144
                        ],
                        [
                            2.9377921292631313,
                            51.23102909334536
                        ],
                        [
                            2.9395494398210404,
                            51.22978103014327
                        ],
                        [
                            2.9395326861153492,
                            51.22973522407282
                        ],
                        [
                            2.9307116955342982,
                            51.23588365892173
                        ],
                        [
                            2.93077732400986,
                            51.235914858980586
                        ],
                        [
                            2.930921969180147,
                            51.23581685905391
                        ],
                        [
                            2.932475593354336,
                            51.23662429379119
                        ],
                        [
                            2.932567101705748,
                            51.23657315009855
                        ]
                   ]
               },
               "properties": {
Too much columns to type, cannot claim them all here because of character limit.
                   }
           }

Edit 3: The Solution provided was a good step in the right direction but it gives me two problems:

  1. The saved geometry is Null making this JSON a table without geometry.
  2. Only 1 data_point is saved and not all data_points that are being requested.

Here is the JSON:

{"type": "FeatureCollection", "features": [{"type": "Feature", "geometry": null, "properties": {"gipodId": 3099549, "StartDateTime": null, "EndDateTime": null, "state": null, "location": {"cities": ["Waregem", "Wielsbeke"], "coordinate": {"coordinates": [3.4206971887218445, 50.91662742195287], "type": "Point", "crs": {"type": "name", "properties": {"name": "urn:ogc:def:crs:OGC:1.3:CRS84"}}}}}}]}

Python Code: Below to see how far it has gone:

import requests
import json
import os
import glob
import shutil

def process_location_data(location_json): 
   """Converts the data point into required geojson format"""

  
   # assigning variable to store geometry details
   geometery_details = location_json.get("location").get("geometery")
   #geometery_details.pop("crs")  # removes the "crs" from geometry

   # includes city and location_coordinates
   location_details = {
     "cities": location_json.get("location").get("cities"),
     "coordinate": location_json.get("location").get("coordinate")
   }

   #EndDateTime
   end_datetime = location_json.get("EndDateTime")

   #StarDateTime
   start_datetime = location_json.get("StarDateTime")

   #State
   state = location_json.get("State")

   #gipodId
   gipod_id = location_json.get("gipodId")
   
   #adding all these details into another dict
   properties = {
     "gipodId": gipod_id,
     "StartDateTime": start_datetime,
     "EndDateTime": end_datetime,
     "state": state,
     "location": location_details
   }


   # creating the final dict to be returned.
   geojson_data_point = {
       "type": "Feature",
       "geometry" : geometery_details,
       "properties": properties
   }

   return geojson_data_point

def process_all_location_data(all_location_points):
    """
    For all the points in the location details we will  
    create the feature collection
    """

    feature_collection = {
         "type": "FeatureCollection",
         "features": []
    } #creates dict with zero features.

    for data_point in all_location_points:
        feature_collection.get("features").append(
            process_location_data(data_point)
        )

    return feature_collection


def fetch_details(url: str, file_name: str):
      # Makes request call to get the data of detail
        response = requests.get(url)
        print("Sending Request for details of gpodId: " + file_name)
        folder_path ='api_request_jsons/fetch_details/JSON unfiltered'
        text = json.dumps(response.json(),sort_keys=False, indent=4)
        print("Details extracted for: "+ file_name)
        save_file(folder_path,file_name,text)
        return response.json()
        # save_file(folder_path,GipodId,text2)
        # any other processe

def fetch_points(url: str):
       response = requests.get(url)
       folder_path ='api_request_jsons/fetch_points'
       text = json.dumps(response.json(),sort_keys=False, indent=4)
       print("Points Fetched, going to next step: Extracting details")
       for obj in response.json():
         all_location_points = [fetch_details(obj.get("detail"),str(obj.get("gipodId")))]
       save_file(folder_path,'points',text)
       feature_collection_json = process_all_location_data(all_location_points)
       text2 = json.dumps(process_all_location_data(all_location_points))
       folder_path2 = "api_request_jsons/fetch_details/Coordinates"
       file_name2 = "Converted"
       save_file(folder_path2,file_name2,text2)
       return feature_collection_json

def save_file(save_path: str, file_name: str, file_information: str):
        completeName = os.path.join(save_path, file_name +".json")
        print(completeName + " saved")
        file1 = open(completeName, "wt")
        file1.write(file_information)
        file1.close()

api_response_url = "http://api.gipod.vlaanderen.be/ws/v1/workassignment"
fetch_points(api_response_url)
like image 536
ThunderSpark Avatar asked Nov 24 '25 19:11

ThunderSpark


1 Answers

Based on #Edit 3 of the question, I have updated the complete solution. There are two issues.

  • Typo in function process_location_data. In this function, I was loading geometery instead of geometry (check the typo in spelling)

       # incorrect for code
       geometery_details = location_json.get("location").get("geometery")
    
       # correct one
       geometery_details = location_json.get("location").get("geometry")

Also, I found typos in state, endDateTime and startDateTime keys of the object. (fixed in the complete solution provided below)

  • Appending the list all_location_points in the fetch_points function.

    #Current code
    for obj in response.json():                                                                                               
        all_location_points = [                                                                                               
            fetch_details(obj.get("detail"), str(obj.get("gipodId")))                                                            
        ]            

In this all_location_points is getting updated each time with the obj, instead of appending it to the list.

# Fixed 

    all_location_points = []                                                                                                  
    for obj in response.json():                                                                                               
        all_location_points.append(                                                                                           
            fetch_details(obj.get("detail"), str(obj.get("gipodId")))                                                            
        )    

In the above answer, I had used shorthand for creating a list (list comprehension). the above 3 lines could be converted into a single line as

    all_location_points = [
      fetch_details(obj.get("detail"), str(obj.get("gipodId")))
      for obj in response.json()
    ]

The complete updated solution, (including typo fix and appending list)


    import requests
    import json
    import os
    import glob
    import shutil
    
    
    def process_location_data(location_json):
        """Converts the data point into required geojson format"""
    
        # assigning variable to store geometry details
        geometery_details = location_json.get("location").get("geometry")
        # geometery_details.pop("crs")  # removes the "crs" from geometry
    
        # includes city and location_coordinates
        location_details = {
            "cities": location_json.get("location").get("cities"),
            "coordinate": location_json.get("location").get("coordinate"),
        }
    
        # EndDateTime
        end_datetime = location_json.get("endDateTime")
    
        # StarDateTime
        start_datetime = location_json.get("startDateTime")
    
        # State
        state = location_json.get("state")
    
        # gipodId
        gipod_id = location_json.get("gipodId")
    
        # adding all these details into another dict
        properties = {
            "gipodId": gipod_id,
            "StartDateTime": start_datetime,
            "EndDateTime": end_datetime,
            "state": state,
            "location": location_details,
        }
    
        # creating the final dict to be returned.
        geojson_data_point = {
            "type": "Feature",
            "geometry": geometery_details,
            "properties": properties,
        }
    
        return geojson_data_point
    
    
    def process_all_location_data(all_location_points):
        """
        For all the points in the location details we will
        create the feature collection
        """
    
        feature_collection = {
            "type": "FeatureCollection",
            "features": [],
        }  # creates dict with zero features.
    
        for data_point in all_location_points:
            feature_collection.get("features").append(process_location_data(data_point))
    
        return feature_collection
    
    
    def fetch_details(url: str, file_name: str):
        # Makes request call to get the data of detail
        response = requests.get(url)
        print("Sending Request for details of gpodId: " + file_name)
        folder_path = "api_request_jsons/fetch_details/JSON unfiltered"
        text = json.dumps(response.json(), sort_keys=False, indent=4)
        print("Details extracted for: " + file_name)
        save_file(folder_path, file_name, text)
        return response.json()
        # save_file(folder_path,GipodId,text2)
        # any other processe
    
    
    def fetch_points(url: str):
        response = requests.get(url)
        folder_path = "api_request_jsons/fetch_points"
        text = json.dumps(response.json(), sort_keys=False, indent=4)
        print("Points Fetched, going to next step: Extracting details")
        all_location_points = []
        for obj in response.json():
            all_location_points.append(
                fetch_details(obj.get("detail"), str(obj.get("gipodId")))
            )
        save_file(folder_path, "points", text)
        feature_collection_json = process_all_location_data(all_location_points)
        text2 = json.dumps(process_all_location_data(all_location_points))
        folder_path2 = "api_request_jsons/fetch_details/Coordinates"
        file_name2 = "Converted"
        save_file(folder_path2, file_name2, text2)
        return feature_collection_json
    
    
    def save_file(save_path: str, file_name: str, file_information: str):
        completeName = os.path.join(save_path, file_name + ".json")
        print(completeName + " saved")
        file1 = open(completeName, "wt")
        file1.write(file_information)
        file1.close()
    
    
    api_response_url = "http://api.gipod.vlaanderen.be/ws/v1/workassignment"
    fetch_points(api_response_url)

like image 192
VJ Magar Avatar answered Nov 27 '25 08:11

VJ Magar