Python:多线程批量下载高德地图最新的省、市、县行政边界地图数据

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)

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值