将 JSON 转换为 QGIS GeoJSON:同时具有多种功能和不同类型

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

目前我有一个程序从特定的 API 请求 JSONS。 API 的创建者声称此数据在 GeoJSON 中,但 QGIS 无法读取它。

所以我想扩展我的 Python 脚本以将 JSON 以可读格式转换为 GEOJSON 以进入 QGIS 并在那里进一步处理它。

但是,我有一些问题, 一个我不知道从哪里开始以及 JSON 文件是如何构建的……一团糟。 JSONS实际上是基于两个API Get Request。一个 Api Get Request 请求点数,第二个请求点数的详细信息。请在此处查看此问题以获取更多背景信息:

请注意,由于字符限制,我无法post此处的代码:

这些细节当然应该是这些点周围区域的“轮廓”。 问题是..点本身在 JSON 中被提及,因此很难指定每个点的坐标。更不用说我们感兴趣的所有其他属性,都在 GeoJSON.

的“点”部分

看看 JSON 本身就明白我的意思了:

{
    "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
}

提醒一下,这些 JSON 不应该是点文件,几何图形可以是上面看到的多边形:多多边形或多线串(此处没有示例)。

那么我如何开始并确保我不仅得到细节属性而且清楚地得到轮廓几何和坐标?

唯一的唯一 ID 是 GIPOD ID,因为它是 API 数据库中实际 link 所在的位置。

编辑 1:

这就是所谓的 geojson 标准。

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

根据这个标准,转换后的JSON应该是这样的:

    "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.
                   }
           }

编辑 3:提供的解决方案是朝着正确方向迈出的良好一步,但它给我带来了两个问题:

  1. 保存的几何体为 Null 使这个 JSON 成为 table 没有 几何.
  2. 只有 1 个 data_point 被保存,而不是所有 data_point 正在请求。

这里是 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代码:下面看看它走了多远:

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)

您可以创建另一个函数并将数据传递给它。

# assuming you got the list of points
# and it's stored in all_location_points 

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
   gipood_id = location_json.get("gipodId")
   
   #adding all these details into another dict
   properties = {
     "gipodId": gipood_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):
      """
      Makes request call to get the data of detail
     (updated to return details)
      """
      response = requests.get(url)
      return response.json()
       

def fetch_points(url: str):
     """
     Updating this function to fetch and process all points
     and convert them to json
     """
      
       
     response = requests.get(url)
     # shot hand to create list of all the detail points
     all_location_points = [
        fetch_details(obj.get("detail")) for obj in response.json()
     ]

     feature_collection_json = process_all_location_data(all_location_points)
     return feature_colkection_json

api_url = "api.gipod.vlaanderen.be/ws/v1/workassignment"
fetch_points(api_url)

编辑 4:虽然 JSON 最终转换为似乎接近 GeoJSON 标准 RFC 7946 的内容。

https://www.rfc-editor.org/rfc/rfc7946

你应该做一件事来将它转换成 QGIS 可以读取的东西。

根据问题的#Edit 3,我更新了完整的解决方案。有两个问题。

  • 函数中的拼写错误 process_location_data。在此函数中,我正在加载 geometery 而不是 geometry(检查拼写错误)

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

此外,我在对象的 stateendDateTimestartDateTime 键中发现了拼写错误。 (固定在下面提供的完整解决方案中)

  • fetch_points 函数中附加列表 all_location_points

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

在此 all_location_points 中,每次使用 obj 进行更新,而不是将其附加到列表中。

# Fixed 

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

在上面的回答中,我使用了 shorthand 来创建列表(列表理解)。以上 3 行可以转换为单行,如

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

完整的更新解决方案,(包括拼写错误修复和附加列表)


    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)