获取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<String,List<String>> 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<Node> relationNodeList = doc.body().childNodes().get(3).childNodes().get(1).childNodes().get(9).childNodes().get(3).childNodes()
                        .get(17).childNodes();
                int size = relationNodeList.size();
                List<String> 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<String> cityCodeArr = (List<String>)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<Node> 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或者邮箱是(344326924@qq.com

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,911评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,014评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 142,129评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,283评论 1 264
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,159评论 4 357
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,161评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,565评论 3 382
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,251评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,531评论 1 292
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,619评论 2 310
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,383评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,255评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,624评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,916评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,199评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,553评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,756评论 2 335