1、地图资源
用到mapinfo制作地图数据经常因为行政区划图的变更而烦恼,因此,获取实时最新的行政区划图边界地理数据非常有必要。
地图数据资源来自阿里云可视化平台(地图数据为高德数据)高德地图数据链接:DataV.GeoAtlas地理小工具系列
在该网站上可以手动下载某个省、市、县边界数据,下载的数据为json格式,可以通过在线转换工具mapshaper 转换成arcgis、mapinfo等数据格式,从而应用。
2、进度条对话框
用进度条来显示数据下载进度。
class Down_progressbar(QDialog):#进度条
'''
myurl 地图资源网址url = 'https://geo.datav.aliyun.com/areas_v3/bound/geojson?code=100000_full' #复制的API
'''
def __init__(self,parent=None,myurl=None):
super(Down_progressbar,self).__init__(parent)
self.url=myurl
self.mythread=DownThread(self.url)#调用自定义多线程
self.mythread.up_signal.connect(self.thread_update)
self.mythread.finish_signal.connect(self.thread_finish)
self.mythread.start()
self.initui()
self.show()
def initui(self):
self.setWindowTitle("Down地图数据")
self.setFixedSize(300,60)
self.label=QLabel("123",self)
self.label.setFont(QFont("宋体", pointSize=10))
self.label.setAlignment(Qt.AlignLeft| Qt.AlignVCenter)
self.pgb = QProgressBar(self)
self.pgb.setAlignment(Qt.AlignLeft| Qt.AlignVCenter)
self.pgb.setValue(1)
self.lay= QGridLayout()
self.setLayout(self.lay)
self.lay.addWidget(self.label,0, 0,1,5)
self.lay.addWidget(self.pgb,1, 0,1,5)
def thread_update(self,list):#更新进度条
value=list[0]
vname=list[1]
self.pgb.setValue(round((value/520)*100,1))
self.label.setText(">>>正在下载 "+vname+" 数据!")
QCoreApplication.processEvents()
def thread_finish(self,filedir):
if filedir:
result=QMessageBox.information(self, "下载完毕", "数据文件保存位置:\n"+os.path.dirname(filedir),QMessageBox.Yes,QMessageBox.Yes)
if result==QMessageBox.Yes:
self.close()
def closeEvent(self, event):#关闭进度条
if self.mythread.isRunning():
self.mythread.quit()
del self.mythread
#self.close()#这个不用写,写了就连主窗口一并推了
3多线程下载数据
class DownThread(QThread):#json下载多线程
up_signal=pyqtSignal(list)
finish_signal=pyqtSignal(str)
def __init__(self,myurl):
super(DownThread,self).__init__()
self.url=myurl
#print(self.url)
def run(self):
i=0
url=self.url
#设置文件保存路径
country_file=get_filedir("jsonfile","china_region.json")#创建国家级文件路径
province_file=get_filedir("jsonfile","province_region.json")#创建省级文件路径
city_file=get_filedir("jsonfile","city_region.json")#创建市级文件路径
xian_file=get_filedir("jsonfile","xian_region.json")#创建县级文件路径
adcode_file=get_filedir("jsonfile","adcode.json")#创建县级文件路径
#设置json临时输入内容数据框架
output_china = {"type": "FeatureCollection",
"features":[]}
output_pro = {"type": "FeatureCollection",
"features":[]}
output_city = {"type": "FeatureCollection",
"features":[]}
output_xian = {"type": "FeatureCollection",
"features":[]}
output_adcode= {"type": "FeatureCollection",
"guo":[],'pro':[],'city':[],'xian':[]}
#设置列表用于存放adcode和name
adcode_list=[]
name_list=[]
#设置正则表达式替换
pattern_all=re.findall('(?<=code=).*$', url)[0]#截取code=后所有内容
pattern_part=re.findall(r"code=(.+?)_",url)[0]#正则表达式,用于替换adcode码
#国家级数据
with open(country_file,'w') as f0:
#更新进度条
i=i+1
self.up_signal.emit([i,"国家"])
#处理数据
url0 = re.sub(str(pattern_all),str(pattern_part),url)
data0= get_data(url0)
for country in data0:
output_china['features'].extend(data0)
guo_tmp=[{"adcode":'100000',"name":"中国","center":[116.3979471,39.9081726],"parent":"世界","pcode":100000,"bound":(70, 10, 130, 60)}]
output_adcode['guo'].extend(guo_tmp)#保存国
#print(country['properties']['name']+'保存完毕')
json.dump(output_china,f0,indent=3)#保存国家数据到json
#通过国家url获取省级数据
with open(province_file,'w') as f1:
#更新进度条
i=i+1
self.up_signal.emit([i,"各省"])
#处理数据
data0_full=get_data(url)#返回的就是json数据格式
output_pro['features'].extend(data0_full)
json.dump(output_pro,f1,indent=3)#保存省数据到json
#获取国家下面所有省的adcode
pro_dict,pro_adcode,pro_name=jsondata_get_adcode(data0_full,parent="中国")
output_adcode['pro'].extend(pro_dict)#保存省
#通过省级url获取市级数据
with open(city_file,'w') as f2:
for pro_adcode1,pro_name1 in zip(pro_adcode,pro_name):
url1 = re.sub(str(pattern_part),str(pro_adcode1),url)
print(pro_name1)
try:#有些省下没有市,比如九段线,比如台湾数据
#更新进度条
i=i+1
self.up_signal.emit([i,pro_name1])
#处理数据
data1_full=get_data(url1)#返回的就是json数据格式
output_city['features'].extend(data1_full)
#获取省下面所有市的adcode
city_dict,city_adcode,city_name=jsondata_get_adcode(data1_full,parent=pro_name1)
output_adcode['city'].extend(city_dict)#保存市
adcode_list.extend(city_adcode)
name_list.extend(city_name)
except Exception as e:
#print(e)
if pro_adcode1=="100000_JD":
continue
else:
#直接把父级地图保存为该级别,比如台湾,这样市级就有了台湾地图
url11 = re.sub(str(pattern_all),str(pro_adcode1),url)
data11_full=get_data(url11)#返回的就是json数据格式
output_city['features'].extend(data11_full)
#把父级别adocde和name传送给下一个子级别
adcode_list.extend([pro_adcode1])
name_list.extend([pro_name1])
print(pro_name1,"没有地级市")
json.dump(output_city,f2,indent=3)#保存市数据到json
#通过市级url获取县级数据
with open(xian_file,'w') as f3:
for city_adcode1,city_name1 in zip(adcode_list,name_list):
url2 = re.sub(str(pattern_part),str(city_adcode1),url)
#print(city_adcode1,city_name1)
try:
#更新进度条
i=i+1
self.up_signal.emit([i,city_name1])
#处理数据
data2_full=get_data(url2)#返回的就是json数据格式
output_xian['features'].extend(data2_full)
#获取市下面所有县的adcode
xian_dict,xian_adcode,xian_name=jsondata_get_adcode(data2_full,parent=city_name1)
output_adcode['xian'].extend(xian_dict)#保存县
#print(xian_dict)
except:
if city_adcode1 !="100000_JD":
url22 = re.sub(str(pattern_all),str(city_adcode1),url)
data22_full=get_data(url22)#返回的就是json数据格式
output_xian['features'].extend(data22_full)
#把父级别adocde和name传送给下一个子级别/到此结束,就不用传递了
json.dump(output_xian,f3,indent=3)#保存县数据到json
#保存adcode数据
with open(adcode_file,'w') as f4:
#更新进度条
i=i+1
self.up_signal.emit([i,"处理所有城市adcode码"])
json.dump(output_adcode,f4,indent=3,ensure_ascii=False)#保存县数据到json #不能加ensure_ascii=False,如果加了在转换shp时转换不了
self.finish_signal.emit(country_file)
self.quit()
def get_data(url):#获取链接中的内容
r = requests.get(url)
data = r.json()['features']
return data
def geo2box(geom):#获取url的json中geomtry的bounds(minx, miny, maxx, maxy)
a=[]
for p in geom:
cord=p['coordinates']
if p['type']=="Polygon":
data=cord[0]
if p['type']=="MultiPolygon":
data=cord[0][0]
result=Polygon(data).bounds
a.append(result)
return(a)
def jsondata_get_adcode(jsdata_full,parent=None):#从get_data返回的数据中获取adcode
adcode=jsonpath.jsonpath(jsdata_full,"$..properties.adcode")
name=jsonpath.jsonpath(jsdata_full,"$..properties.name")
center=jsonpath.jsonpath(jsdata_full,"$..properties.center")
try:#由于内蒙古、天津等地的json格式中parent的adcode并不规范,出现了parent":"{ \"adcode\": 120000 },这里就不得不处理一下
pcode=[]
tmp=jsonpath.jsonpath(jsdata_full,"$..properties.parent")
for a in tmp:
if isinstance(a,str) :
a=eval(a)
pcode.append(a['adcode'])
else:
pcode.append(a['adcode'])
except Exception as e:
print(e)
geom=jsonpath.jsonpath(jsdata_full,"$..geometry")
bound=geo2box(geom)
parent=[parent]*len(name)
if adcode[-1]=="100000":
adcode[-1]="100000_JD"
name[-1]="九段线"
tmppd=pd.DataFrame(data=[adcode,name,center,parent,pcode,bound],index=["adcode",'name','center','parent','pcode','bound']).T
tmpdict=tmppd.to_dict('records')
#print(tmpdict)
return(tmpdict,adcode,name)
4程序运行结果
if __name__=='__main__':
start_time = datetime.datetime.now()
app=QApplication(sys.argv)
url = 'https://geo.datav.aliyun.com/areas_v3/bound/geojson?code=100000_full' #复制的API
a=Down_progressbar(myurl=url)
end_time = datetime.datetime.now()
print('用时: ',end_time-start_time)
sys.exit(app.exec_())
通过调用上述自定义的类,就可以自动下载所有省、市、线边界的json数据,下图为本人运行的结果。
5后续数据应用处理
我们下载下来的json数据要用gis软件打开还是不行的,可以应用geopandas库将其转换为shp等数据格式,这一步也可以用多线程来处理,这里就不具体列出,下面给出json转换为shp的核心代码函数。
def trans_code(jsonfile):#单个geojson转换为shp代码
try:
tmpname=os.path.basename(jsonfile) #获取文件名
filename=os.path.splitext(tmpname)[0]#从文件名中分离后缀
if filename=="adcode":
get_citypoint_shp(jsondir=jsonfile)
else:
shpfile=get_filedir('shpfile',filename+".shp")
data = geopandas.read_file(jsonfile)
data.to_file(shpfile, driver='ESRI Shapefile', encoding='utf-8')
print(filename+"转换完毕")
except Exception as e:
print(e)