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:
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)
Based on #Edit 3 of the question, I have updated the complete solution. There are two issues.
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)
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)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With