从gadm下载世界各国的行政边界

GADM: 全球行政区划数据库

GADM主页https://gadm.org/

GADM,全称Database of Global Administrative Areas,是一个高精度的全球行政区划数据库。其包含了全球所有国家和地区的国界、省界、市界、区界等多个级别的行政区划边界数据。

警告

GADM提供的中国国界数据不符合中国的领土主张,省界、市界、区界等数据也不一定是最新的版本。在正式刊物中发表使用此类数据的图件时需格外谨慎。

数据下载

GADM提供了两种下载方式:

  1. 下载全球所有国家和地区的所有数据 https://gadm.org/download_world.html
  2. 按国家下载 https://gadm.org/download_country.html

由于全球数据量巨大,建议根据需要按照国家下载数据。

需要说明的是,GADM 中对country 的定义为“any entity with an ISO country code”。因而如果想要下载完整的中国数据,实际上需要下载China、Hong Kong、Macao和Taiwan 四个数据。

由于GADM提供的中国国界数据不符合我国领土主张,本文以美国数据为例介绍数据下载及使用。

数据格式及转换

对于每个数据,GADM提供了5种不同的格式:

  • Geopackage:可以被GDAL/OGR、ArcGIS、QGIS等软件读取
  • Shapefile:可直接用于ArcGIS等软件
  • KMZ:可直接在Google Earth中打开
  • R (sp):可直接用于R语言绘图
  • R (sf):可直接用于R语言绘图

如果在安装GMT时,GMT已经正确链接了GDAL库,则Shapefile格式的数据可以直接用于绘图。实际绘图时,可能只想要一小部分数据(比如某个省/州的界线),这种情况下,则需要将数据转换成纯文本文件,以方便从数据中提取出需要的部分。

python 爬虫,下载各国的行政边界

目标地址是 https://gadm.org/download_country.html

image-20231114093456953

按F12,分析网站接口。

找到了changeCountry()这个函数


function changeCountry() {
	var x = document.getElementById("countrySelect").value
	if (x == "") {
		document.getElementById("gpkg").innerHTML ="";
		document.getElementById("shp").innerHTML ="";
//		document.getElementById("json").innerHTML = "";	
		document.getElementById("kmz").innerHTML = "";	
		document.getElementById("crs").innerHTML = "";
		document.getElementById("img").src = "";
	} else {
		var version = 4.1
		var vers = 41
		var a = x.split("_");
		var base = "https://geodata.ucdavis.edu/gadm/gadm4.1/";
		document.getElementById("gpkg").innerHTML ="<a href=" + base + "gpkg/gadm" + vers + "_" + a[0] + ".gpkg>Geopackage</a>";
		document.getElementById("shp").innerHTML ="<a href=" + base + "shp/gadm" + vers + "_" + a[0] + "_shp.zip>Shapefile</a>";
		var n = parseInt(a[2]);	
		var kmztxt = "KMZ: <a href=" + base + "kmz/gadm" + vers + "_" + a[0] + "_0.kmz>level-0</a>";
		var jsontxt = "GeoJSON: <a href=" + base + "json/gadm" + vers + "_" + a[0] + "_0.json>level-0</a>";
		for (i = 1; i < n; i++) { 
			jsontxt +=  ", <a href=" + base + "json/gadm" + vers + "_" + a[0] + "_" + i.toString() + ".json.zip>level" + i.toString() +"</a>";
		}		
		
		for (i = 1; i < n; i++) { 
			kmztxt +=  ", <a href=" + base + "kmz/gadm" + vers + "_" + a[0] + "_" + i.toString() + ".kmz>level" + i.toString() +"</a>";
		}		
		document.getElementById("json").innerHTML = jsontxt;
		document.getElementById("kmz").innerHTML = kmztxt;
		document.getElementById("img").src = "https://geodata.ucdavis.edu/gadm/gadm" + version + "/png/" + a[0] + "_adm.png";
		document.getElementById("crs").innerHTML = 'The coordinate reference system is <a href="https://en.wikipedia.org/wiki/Geographic_coordinate_system">longitude/latitude</a> and the <a href="https://en.wikipedia.org/wiki/World_Geodetic_System">WGS84</a> datum.<br>Description of <a href="/formats.html">file formats</a>.';
	}
}

从这个函数中,得到以下信息:

1 x是countrySelect的value值

2 countrySelect是Country单选框中的国家名字

3 a[0]是x中的下横线前的国家缩写字母

4 目标文件的下载地址是

"https://geodata.ucdavis.edu/gadm/gadm4.1/kmz/gadm/41_" + a[0] + "_shp.zip

开始编码

明确目标:自动的下载各国的行政边界shape文件。

编程语言:python

需要科学上网

完整代码如下:

#!/usr/bin/env python
# -*- coding: utf-8 -*- 
# @Time : 2023/11/14 19:26 
# @File : download_gadm.py

import requests
import os, re

proxies = {'http': 'socks5h://127.0.0.1:7890',
           'https': 'socks5h://127.0.0.1:7890'
           }

class download_gadm():
    def __init__(self):
        self.url = 'https://gadm.org/download_country.html'
        self.proxies = proxies
        self.headers = {
            'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 '
                            '(KHTML, like Gecko) Chrome/99.0.4844.51 Safari/537.36'
        }

    def request_save(self,outpath):

        if os.path.exists(outpath):
            pass
        else:
            # create a new folder
            os.makedirs(outpath)

        html = requests.get(url=self.url, headers=self.headers, proxies=self.proxies).text

        country_lists = re.findall(r'<option value="(.*)">', html)
        for countrys in country_lists:
            try:
                if '_' in countrys:
                    try:
                        country_abbreviation = countrys.split('_')[0]
                        country_name = countrys.split('_')[1]
                        url = self.target_url(country_abbreviation)
                        print(url)
                        context = requests.get(url=url,         headers = self.headers, proxies=self.proxies)
                        with open(outpath + f'/{country_name}.zip', 'wb') as f:
                            f.write(context.content)
                            print('completed download %s'%country_name)

                    except Exception as e:
                        print(e)
                else:
                    print('空值')

            except:
                try:
                    country_abbreviation = countrys.split('_')[0]
                    country_name = countrys.split('_')[1]
                    url = self.target_url(country_abbreviation)
                    print(url)
                    context = requests.get(url=url, headers=self.headers)
                    with open(outpath + f'/{country_name}.zip', 'wb') as f:
                        f.write(context.content)
                        print('completed download %s' % country_name)

                except Exception as e:
                    print(e)



    def target_url(self, country):
        r'https://geodata.ucdavis.edu/gadm/gadm4.1/shp/gadm41_AUS_shp.zip'
        url = r'https://geodata.ucdavis.edu/gadm/gadm4.1/shp/gadm41_%s_shp.zip'%country
        return url

if __name__ == '__main__':
    outpath = r'D:\gadm'
    download_gadm().request_save(outpath)

方便使用

为了方便使用,用pyside6写一个前端,结合以上代码打包为exe文件。

代码如下:

#!/usr/bin/env python
# -*- coding: utf-8 -*- 
# @Time : 2023/12/13 22:15
# @File : world_map_gui.py

import os
import re
import sys
import requests
from PySide6.QtCore import Signal, QThread
from PySide6.QtGui import QIcon
from PySide6.QtWidgets import QApplication, QGridLayout, QPushButton, QTextEdit, QDialog, QLineEdit

proxies = {'http': 'socks5h://127.0.0.1:7890',
           'https': 'socks5h://127.0.0.1:7890'
           }

class MapDownload(QThread):
    _signal = Signal()
    text_signal = Signal(str)
    def __init__(self, savepath, *args, **kwargs):
        QThread.__init__(self, *args, **kwargs)
        self.savepath = savepath
        self.url = 'https://gadm.org/download_country.html'
        self.proxies = proxies
        self.headers = {
            'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 '
                          '(KHTML, like Gecko) Chrome/99.0.4844.51 Safari/537.36'
        }



    def target_url(self, country):
        r'https://geodata.ucdavis.edu/gadm/gadm4.1/shp/gadm41_AUS_shp.zip'
        url = r'https://geodata.ucdavis.edu/gadm/gadm4.1/shp/gadm41_%s_shp.zip' % country
        return url

    def run(self):
        if os.path.exists(self.savepath):
            pass
        else:
            # create a new folder
            os.makedirs(self.savepath)

        html = requests.get(url=self.url, headers=self.headers, proxies=self.proxies).text

        country_lists = re.findall(r'<option value="(.*)">', html)
        for countrys in country_lists:
            try:
                if '_' in countrys:
                    try:
                        country_abbreviation = countrys.split('_')[0]
                        country_name = countrys.split('_')[1]
                        url = self.target_url(country_abbreviation)

                        context = requests.get(url=url, headers=self.headers, proxies=self.proxies)
                        with open(self.savepath + f'/{country_name}.zip', 'wb') as f:
                            f.write(context.content)
                            self.text_signal.emit('已下载 %s' % country_name)
                            print('completed download %s' % country_name)

                    except Exception as e:
                        print(e)
                else:
                    print('空值')

            except:
                try:
                    country_abbreviation = countrys.split('_')[0]
                    country_name = countrys.split('_')[1]
                    url = self.target_url(country_abbreviation)
                    print(url)
                    context = requests.get(url=url, headers=self.headers)
                    with open(self.savepath + f'/{country_name}.zip', 'wb') as f:
                        f.write(context.content)
                        self.text_signal.emit('completed download %s' % country_name)
                        print('completed download %s' % country_name)

                except Exception as e:
                    print(e)

        self._signal.emit()

class Ui_MainWindow(QDialog):
    def __init__(self):
        super(Ui_MainWindow, self).__init__()
        self.setupUi(self)


        # TypeError: native Qt signal instance 'clicked' is not callable
        self.pushButton.clicked.connect(self.download_map)
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(400, 300)
        MainWindow.setMinimumSize(400, 300)

        self.gridLayout = QGridLayout()
        self.gridLayout.setObjectName("gridLayout")
        self.setLayout(self.gridLayout)

        # 图标
        self.setWindowTitle('世界各国矢量下载')
        self.setWindowIcon(QIcon(':/pic/icon1.png'))
        self.pushButton = QPushButton()
        self.pushButton.setText('运行')
        self.pushButton.setObjectName("pushButton")
        self.savepathtext = QLineEdit()
        self.savepathtext.setPlaceholderText('输入保存路径')

        self.messageTE = QTextEdit()
        self.messageTE.setObjectName("text")
        self.gridLayout.addWidget(self.savepathtext,  0, 1, 1, 1)

        self.gridLayout.addWidget(self.pushButton, 0, 2, 1, 1)
        self.gridLayout.addWidget(self.messageTE, 1, 1, 1, 1)


    def download_map(self):
        save_path = str(self.savepathtext.text())
        if save_path != "":
            # 按钮不可选
            self.pushButton.setDisabled(True)
            self.savepathtext.setDisabled(True)



            self.thread = MapDownload(save_path)
            self.thread._signal.connect(self.completed)
            self.thread.start()
            self.thread.text_signal.connect(self.messageTE.append)

    def completed(self):
        self.messageTE.append('下载完成')
        self.savepathtext.setDisabled(False)
        self.pushButton.setEnabled(True)

if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = Ui_MainWindow()
    window.show()
    app.exec()

image-20231214152307841

最后

在公众号回复“gdam”获取下载后的数据。该数据不包括咱们中国的矢量,这个教程也只是作为分享。

image-20231214152914364