获取OpenStreetMap最新矢量行政区划图(2020中国行政区划shp)

之前项目上发布地图服务时候,被客户嫌弃我们使用的地图shp数据版本太老了(2014版)
含泪啜泣

于是乎,领导拍板了,更新!必须更新!

找遍各大网络数据,最新版的中国行政区划图(省市县级),最新版更新到2015年,嗯,还是不知道是否真的2015。然后,爬遍了城墙内外,万能宝一份2017的数据100+,地理空间数据云的,一份县级中国区划2014版,报价十几万……
穷就一个字

鉴于真实的2020数据,大概率小贫民是不可获得的(涉密),只能从开源数据上想想办法了。

鉴于之前爬遍全网的经历,目光直接锁定OpenStreetMap的数据,开源、免费、并且经过验证,矢量边界比手中现有的行政区划更加精细。缺点则是涉及到领土有争议的部分领域,OSM是站在对立面的,童鞋们慎重啊!

来吧,话不多说,上干货!

1. 技术路线介绍

根据之前掌握的信息,OSM官方是提供矢量图形下载的。大概是我使用的导出方式不对,一直没能下下来。

已选中区域下载

经过多方找了GeoJSON中的OpenStreetMap管理边界
以及提取OSM行政边界的方法, 确认OSM当前的行政边界的下载的方式。整体路线大概是

1. 获取所有行政区划的relation code,
2. 通过CURL命令下载。(大概率需要科学上网)
备案方法2:通过[http://polygons.openstreetmap.fr/?id=RELATIONID](http://polygons.openstreetmap.fr/?id=)
获取到图形自动入库。(此方法建议无法科学上网的童鞋们使用。)

2. 获取关系编码

通过观察网页中的相关信息,咱们可以快速的看到,每一个区域的编号,以及与它接壤的外围编号,以及它的子一级编号


OSM区域上下关系code

鉴于此,中国34个省市的关系网尽在掌握。
数据获取逻辑如下:

  1. 获取中国34个省市县的关系网;
  2. 向下查找每个省市对应的市级关系编码;
  3. 根据市级关系编码,再向下查找县级编码。
    基于此,便可获取全中国34个省市县区最新的shp矢量行政区划了。

一个简单的爬虫小程序便应运而生。

package com.openstreetmap.reptile;

import lombok.extern.slf4j.Slf4j;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Node;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @Author 
 * @Date: 2020/1/16 13:55
 * @Description: 用于处理爬虫逻辑
 **/
@Slf4j
public class FileReptileCore {
    public static void main(String[] args) {
        try {
            //34个省市县的编码
            String code ="913011;553303;153314;911844;286937;2128285;912998;199073;407492;913106;913073;913012;913109;198590;912942;" +
                    "153269;913100;913006;913105;913068;913094;553302;153292;153310;286342;161349;" +
                    "913101;912940;912999;913067;913069;913110;1867188";

            String[] codeArr = code.split(";");
            Map> cityLevelCode = new HashMap<>();
            //基础的访问网址
            String baseUrl = "https://www.openstreetmap.org/relation/";

            for (int i = 0; i < codeArr.length; i++) {
                //输出当前正在爬取的省份编码
                log.info("===========开始输出:" + codeArr[i]);
                String url =  baseUrl + codeArr[i];
                org.jsoup.nodes.Document doc = Jsoup.connect(url).ignoreContentType(true).timeout(30000).get();
                //获取节点数据
                List relationNodeList = doc.body().childNodes().get(3).childNodes().get(1).childNodes().get(9).childNodes().get(3).childNodes()
                        .get(17).childNodes();
                int size = relationNodeList.size();
                List cityCode = new ArrayList<>();
                //循环读取市级节点
                for (int j = 0; j < size; j++) {
                    Node node = relationNodeList.get(j);
                    if(node.toString().equals(" ")){
                        continue;
                    }
                    String value = node.childNodes().get(1).attributes().get("href");
                    if(value.contains("relation")){
                        String relationCode = value.split("/")[2];
                        cityCode.add(relationCode);
                        System.out.println(relationCode);
                    }
                }
                cityLevelCode.put(codeArr[i],cityCode);
            }
            //根据市级节点的code,获得县级的code
            for (Map.Entry province :cityLevelCode.entrySet()) {
                List cityCodeArr = (List)province.getValue();
                int cityCodeArrsize = cityCodeArr.size();
                for (int a = 0; a < cityCodeArrsize; a++) {
                        log.info("===========开始输出:" + cityCodeArr.get(a));
                        String url =  baseUrl + cityCodeArr.get(a);
                        org.jsoup.nodes.Document doc = Jsoup.connect(url).ignoreContentType(true).timeout(30000).get();
                        List relationNodeList = doc.body().childNodes().get(3).childNodes().get(1).childNodes().get(9).childNodes().get(3).childNodes()
                                .get(17).childNodes();
                        int size = relationNodeList.size();
                        for (int j = 0; j < size; j++) {
                            Node node = relationNodeList.get(j);
                            if(node.toString().equals(" ")){
                                continue;
                            }
                            String value = node.childNodes().get(1).attributes().get("href");
                            if(value.contains("relation")){
                                String relationCode = value.split("/")[2];
                                System.out.println(relationCode);
                            }
                        }
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

基于此,所有省市县的编码获取完毕。

3. 根据编码下载shp

根据这位大大的文章OSM行政边界的方法, 使用CURL命令即可

curl -f -o file.zip --url 
"https://wambachers-osm.website/boundaries/exportBoundaries?cliVersion=1.0&cliKey=52d97a3b-3fc1-44cd-bd9a-e8c1c9468bab&exportFormat=shp&exportLayout=levels&exportAreas=land&union=false&selected=3287346"

其中:
curl -f -o 代表下载操作;
file.zip: 表示下载的文件名;
--url: 代表将此url下面的文件下载下来
整个链接:https://wambachers-osm.website/boundaries/exportBoundaries?cliVersion=1.0&cliKey=52d97a3b-3fc1-44cd-bd9a-e8c1c9468bab&exportFormat=shp&exportLayout=levels&exportAreas=land&union=false&selected=3287346中,需要修改的部分便是327346,这个就是上文获取的code。多文件下载时,仅需将多个code使用 , 隔开即可
比如:

curl -f -o file.zip --url 
"https://wambachers-osm.website/boundaries/exportBoundaries?cliVersion=1.0&cliKey=52d97a3b-3fc1-44cd-bd9a-e8c1c9468bab&exportFormat=shp&exportLayout=levels&exportAreas=land&union=false&selected=3287346,2963917

等等……

备注

至于备选方法,只要拿到了关系代码,也是一个简单查询命令便可转存入库。在此便不赘述。

关于上文介绍的数据,如有需要,有偿私信哈~Q或者邮箱是([email protected]

你可能感兴趣的:(获取OpenStreetMap最新矢量行政区划图(2020中国行政区划shp))