大家好,我是小白鸽,上一期文章挖了一个坑,留了两个问题,分别是如何把json转为GeoJSON以及如何把高德路径规划结果转存成矢量数据,本期我们就来详细说说这两个问题。
其实要解决这两个问题的方法挺多的,但是进行数据分析首先要做的应该是分析数据结构,我们先来看一下路径规划返回的结果示例。
{
"status" :"1",
"info" :"ok",
"infocode" :"10000",
"count" :"1",
"route" :{
"origin" :"116.481028,39.989643",
"destination" :"116.434446,39.90816",
"paths" :[
"0" :{
"distance" :"11068",
"duration" :"8854",
"steps" :[
"0" :{
"instruction" :"向南步行16米左转",
"orientation" :"南",
"road" :[ ],
"distance" :"16",
"duration" :"13",
"polyline" :"116.480885,39.989371;116.480907,39.989353;116.48089,39.989227",
"action" :"左转",
"assistant_action" :[ ],
"walk_type" :"0"
},
"1" :{
"instruction" :"步行292米右转",
"orientation" :[ ],
"road" :[ ],
"distance" :"292",
"duration" :"234",
"polyline" :"116.480885,39.989223;116.480933,39.989201;116.480933,39.989201;116.480955,39.989184;116.481155,39.989058;116.481155,39.989058;116.481428,39.98888;116.481428,39.98888;116.481489,39.988841;116.481489,39.988841;116.482574,39.988125;116.48322,39.987665;116.48322,39.987665;116.48332,39.987565;116.48332,39.987565;116.48342,39.987435",
"action" :"右转",
"assistant_action" :[ ],
"walk_type" :"0"
}
]
}
]
}
}
其实,如果数据量不大,可以手动存成表格数据,再通过ArcGis转存成矢量数据。
那如果数据量很大呢?假设我们有20000个村采集的数据需要转存成矢量数据,那要一个个手动填吗?
大可不必,可以看到路径规划返回的结果是一个json对象,我们本期的目标是把steps里面的内容解析出来,steps里面的其实是一个json对象数组,数组里面每一个元素都是json对象。json数据是以键值对的形式组织数据,每个键值对由一个键(key)和一个值(value)组成,它们之间用冒号 : 分隔。例如:
#name是键,John Doe是值
{
"name": "John Doe"
}
因此对于复杂的json对象也可以通过逐级索引的方式获取目标数据。以下是一个示例:
import json
# 假设这是你的JSON字符串
your_json_string = '''
{
"person": {
"name": "John Doe",
"age": 30,
"address": {
"street": "123 Main St",
"city": "Anytown",
"zipCode": "12345"
},
"phoneNumbers": [
{
"type": "home",
"number": "212 555-1234"
},
{
"type": "office",
"number": "212 555-4567"
}
]
}
}
'''
# 解析JSON字符串
json_data = json.loads(your_json_string)
# 逐层索引以获取办公室电话号码
office_number = json_data["person"]["phoneNumbers"][1]["number"]
print(office_number) # 输出: 212 555-4567
获取到目标数据只是第一步,开头说了要把数据转换成GeoJSON,以及转存矢量数据,本文在此介绍3个python库(模块),分别是arcpy、geojson和geopandas,其中arcpy库和geopandas库都可以实现将结果数据直接转存成矢量数据,geojson库则可以实现将结果数据转换成GeoJSON格式,并且可以进一步结合geopandas库将GeoJSON格式数据转存成矢量数据。
利用arcpy转存矢量数据
arcpy其实是一个python库,通过引用(import)arcpy,可以让我们在python中使用arcgis的空间分析和数据管理等能力,相当于在python中使用ArcGis。本文在此只做简要介绍,以后将会开个专栏讲arcpy的使用以及如何利用arcpy去处理一些日常遇到的问题。
官方介绍请查阅:
https://desktop.arcgis.com/zh-cn/arcmap/latest/analyze/arcpy/what-is-arcpy-.htm
以下是利用arcpy将路径规划结果中的分段数据转存成矢量数据的示例:
import requests
import json
import arcpy
key = '你的高德地图api key'
#起点经纬度
origin = '116.481028,39.989643'
#终点经纬度
destination = '116.434446,39.90816'
#拼接url,上一期输出是xml格式,本期尝试输出json格式
url = 'https://restapi.amap.com/v3/direction/walking?origin='+ origin +'&destination='+ destination +'&key=' + key +'&output=JSON'
#请求url
responce = requests.get(url)
#获取返回内容
str = responce.content
#将返回的字符串转换为json对象
jstr = json.loads(str)
#定位到steps里面的内容
steps = jstr['route']['paths'][0]['steps']
#创建空list存数据
distance,duration,linestr,road = [],[],[],[]
#读取steps里面每一段路径的信息,包括distance、duration、road和polyline
for step in steps:
distance.append(step['distance'])
duration.append(step['duration'])
linestr.append(step['polyline'])
road.append(step['road'])
path="你的文件输出路径"
#输出可以是XXXX.shp
outputname="你的输出文件名称"
#设置空间参考,一般为4326,即wgs84
spatR = arcpy.SpatialReference(4326)
#创建一个空的线要素
line = arcpy.CreateFeatureclass_management(path, outputname,'POLYLINE',"","","",spatR)
#给shp添加字段属性,包括distance,duration,road
arcpy.AddField_management(line,'distance','Short Integer')
arcpy.AddField_management(line,'duration','Short Integer')
arcpy.AddField_management(line,"road",'Text')
fields =['SHAPE@',"distance", "duration", "road"]
#创建游标,arcpy对数据的操作通过游标进行
cur = arcpy.InsertCursor(line)
#通过循环对数据进行操作
for i in range(0,len(linestr)):
#准备好属性数据,实际上如果熟练可以把该循环整合到上一个循环
distance_ = distance[i]
duration_ = duration[i]
road_ = road[i]
#获取单挑线段的坐标集合
line = linestr[i]
#获取该坐标集合中所有的点
pnts = line.split(';')
#创建空的array
array = arcpy.Array()
#对坐标集合中的点进行遍历,创建Point,并添加到array中,形成符合arcgis格式要求的点集
for pnt in pnts:
pnt_ = pnt.split(',')
lon = pnt_[0]
lat = pnt_[1]
point = arcpy.Point(lon,lat)
array.add(point)
#根据点集创建Polyline线
polyline = arcpy.Polyline(array,spatR)
#清空array,为下一轮循环做准备
array.removeAll()
#根据上面的fields格式要求创建newFields并添加到原来的fields中
newFields = [polyline,distance_,duration_,road_]
cur.insertRow(newFields)
del cur
利用geopandas转存矢量数据
geopandas也是一个python库,它是对pandas库的扩展,通过geopandas也可以进行空间分析和数据处理,与arcpy不同的是,不需要安装ArcGis就可以使用这些空间分析能力,它的易用性更好。
官方介绍请查阅:
https://desktop.arcgis.com/zh-cn/arcmap/latest/analyze/arcpy/what-is-arcpy-.htm
以下是利用geopandas将路径规划结果中的分段数据转存成矢量数据的示例:
import requests
import json
from shapely.geometry import LineString;
from geopandas import GeoSeries,GeoDataFrame
key = '你的高德地图api key'
#起点经纬度
origin = '116.481028,39.989643'
#终点经纬度
destination = '116.434446,39.90816'
#拼接url,上一期输出是xml格式,本期尝试输出json格式
url = 'https://restapi.amap.com/v3/direction/walking?origin='+ origin +'&destination='+ destination +'&key=' + key +'&output=JSON'
#请求url
responce = requests.get(url)
#获取返回内容
str = responce.content
#将返回的字符串转换为json对象
jstr = json.loads(str)
#定位到steps里面的内容
steps = jstr['route']['paths'][0]['steps']
#创建空list存数据
distance,duration,polyline,road = [],[],[],[]
#读取steps里面每一段路径的信息,包括distance、duration、road和polyline
for step in steps:
distance.append(step['distance'])
duration.append(step['duration'])
polyline.append(step['polyline'])
road.append(step['road'])
#创建一个空的GeoSeries
g = GeoSeries()
#通过循环向GeoSeries中添加数据
for i in range(0,len(polyline)):
line = polyline[i]
newstr = line.split(';')
location_tuples = [tuple(map(float, loc.split(','))) for loc in newstr]
pl = LineString(location_tuples)
new_geoseries = GeoSeries(pl)
g = pd.concat([g, new_geoseries])
#为避免出错,重置一下索引
g.reset_index(drop=True,inplace=True)
#将数据组装成GeoDataFrame,其中{'distance' :distance,'duration': duration,'road': road}代表属性信息,geometry=g代表空间信息,crs='epsg:4326'代表投影信息
gdf = GeoDataFrame({'distance' :distance,'duration': duration,'road': road}, geometry=g,crs='epsg:4326')
#保存成shp
gdf.to_file('line.shp')
结合geojson和geopandas转存矢量数据
geojson是python的一个模块,geojson模块支持GeoJSON标准定义的所有数据结构,包括几何对象(点、线、多边形等)、特征、特征集合和坐标参考系统。该模块提供了函数来解析和序列化GeoJSON数据,包括从字符串和文件中解析GeoJSON数据,以及将Python对象序列化为GeoJSON字符串或写入文件。
结合geojson和geopandas的处理方式分两步,第一步是将结果数据转换成GeoJSON格式,第二步是将GeoJSON格式数据转存成矢量数据。第一步时可以根据需求将多条线段转换成一个多段线要素(MultiLineString),也可以转换成多个线要素(LineString)。
MultiLineString和LineString的区别在于,MultiLineString一条记录可以对应多条线,LineString一条记录只对应一条线。
-
转换成一个多段线要素示例:
import requests
import json
import geojson
key = '你的高德地图api key'
#起点经纬度
origin = '116.481028,39.989643'
#终点经纬度
destination = '116.434446,39.90816'
#拼接url,上一期输出是xml格式,本期尝试输出json格式
url = 'https://restapi.amap.com/v3/direction/walking?origin='+ origin +'&destination='+ destination +'&key=' + key +'&output=JSON'
#请求url
responce = requests.get(url)
#获取返回内容
str = responce.content
#将返回的字符串转换为json对象
jstr = json.loads(str)
#定位到steps里面的内容
steps = jstr['route']['paths'][0]['steps']
#创建空list存数据
distance,duration,polyline,road = [],[],[],[]
#读取steps里面每一段路径的信息,包括distance、duration、road和polyline
for step in steps:
distance.append(step['distance'])
duration.append(step['duration'])
polyline.append(step['polyline'])
road.append(step['road'])
line_list = []
#数据清洗,重新组织数据格式
for line in polyline:
newstr = line.split(';')
location_tuples = [tuple(map(float, loc.split(','))) for loc in newstr]
line_list.append(location_tuples)
#创建MultiLineString
MultiLineString_geojson = geojson.MultiLineString(coordinates=line_list)
#创建FeatureCollection
Multi_feature_collection = geojson.FeatureCollection(features=[geojson.Feature(geometry=MultiLineString_geojson)])
#添加属性示例 属性名称name,属性值OD_name
Multi_feature_collection['features'][0]['properties']['name'] = 'OD_name'
#尝试打印
print(geojson.dumps(Multi_feature_collection, indent=2))
-
转换成多个线要素示例
import requests
import json
import geojson
key = '你的高德地图api key'
#起点经纬度
origin = '116.481028,39.989643'
#终点经纬度
destination = '116.434446,39.90816'
#拼接url,上一期输出是xml格式,本期尝试输出json格式
url = 'https://restapi.amap.com/v3/direction/walking?origin='+ origin +'&destination='+ destination +'&key=' + key +'&output=JSON'
#请求url
responce = requests.get(url)
#获取返回内容
str = responce.content
#将返回的字符串转换为json对象
jstr = json.loads(str)
#定位到steps里面的内容
steps = jstr['route']['paths'][0]['steps']
#创建空list存数据
distance,duration,polyline,road = [],[],[],[]
#读取steps里面每一段路径的信息,包括distance、duration、road和polyline
for step in steps:
distance.append(step['distance'])
duration.append(step['duration'])
polyline.append(step['polyline'])
road.append(step['road'])
#创建一个空的LineString和FeatureCollection,用于存放后续数据
totalLineString_geojson = geojson.LineString()
total_feature_collection = geojson.FeatureCollection(features=[geojson.Feature(geometry=totalLineString_geojson)])
#通过循环将steps中每一段路径都转换成Feature,并添加进空的FeatureCollection
for i in range(0,len(polyline)):
line = polyline[i]
newstr = line.split(';')
location_tuples = [tuple(map(float, loc.split(','))) for loc in newstr]
one_LineString_geojson = geojson.LineString(coordinates=location_tuples)
one_feature = geojson.Feature(geometry=one_LineString_geojson)
#为每一段路径添加属性
one_feature['properties']['distance'] = distance[i]
one_feature['properties']['duration'] = duration[i]
one_feature['properties']['road'] = road[i]
#添加进FeatureCollection
total_feature_collection['features'].append(one_feature)
#删除最初始的空feature
total_feature_collection['features'].remove(total_feature_collection['features'][0])
#尝试打印
print(geojson.dumps(total_feature_collection, indent=2))
-
在上述代码基础上,只需如下代码即可将数据转存为矢量数据
#引用geopandas
import geopandas
# total_feature_collection 上一步生成的FeatureCollection
#将FeatureCollection转换成GeoDataFrame
#crs='epsg:4326'设置坐标为wgs84,最好提前进行坐标转换
total_gdf = geopandas.GeoDataFrame.from_features(total_feature_collection,crs='epsg:4326')
#重置一下索引
total_gdf.reset_index(drop=True,inplace=True)
#保存成line.shp
total_gdf.to_file('line.shp')
结果展示
最后,其实数据处理的方法有很多,实现同一个目标也有多种不同的路径,最主要的是根据实际的需求选择适合自己的方法。本期分享就到此结束,我是小白鸽,欢迎大家点赞关注,欢迎评论交流。