├── .gitignore ├── README.md ├── libs ├── enumApi.js └── util.js ├── package.json └── src ├── app.js ├── downLoadFile.js ├── getAreaGeoData.js ├── getChinaAllArea.js ├── parseFile.js └── parseGeoData.js /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | .vscode 3 | package-lock.json 4 | /map -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### 说明 2 | 该项目是一个用于下载[阿里DataV.GeoAtlas](http://datav.aliyun.com/tools/atlas)地图GeoJson文件的node程序 3 | 4 | ### 使用 5 | 1. 安装相关modules:`npm install` 6 | 2. 执行命令:`npm run server 「区域id」`(暂不支持全国id 100000) 7 | 8 | 如`npm run server 430000`(下载湖南下辖所有市、区、县的地图文件) `npm run server 430100`(下载长沙下辖所有区县的地图文件) 9 | 4. 最后会将文件下载到`map`文件夹下(以省id作为文件夹) 10 | 5. 根据区域id获取该地区下直属子区域的名称和areaId:`npm run area 「区域id」`(会在控制台直接打印出),例如`npm run server 430000`(获取湖南省直属子区域信息) 11 | ![20210312094556.png](https://wujun-pic-bed.oss-cn-chengdu.aliyuncs.com/img/20230128160927.png) 12 | 13 | 14 | ### 中国所有省/直辖市/特别行政区的名称与areaId 15 | 16 | | 名称 | areaId| 17 | | ---- | ---- | 18 | | 北京市 | 110000 | 19 | | 天津市 | 120000 | 20 | | 河北省 | 130000 | 21 | | 山西省 | 140000 | 22 | | 内蒙古自治区 | 150000 | 23 | | 辽宁省 | 210000 | 24 | | 吉林省 | 220000 | 25 | | 黑龙江省 | 230000 | 26 | | 上海市 | 310000 | 27 | | 江苏省 | 320000 | 28 | | 浙江省 | 330000 | 29 | | 安徽省 | 340000 | 30 | | 福建省 | 350000 | 31 | | 江西省 | 360000 | 32 | | 山东省 | 370000 | 33 | | 河南省 | 410000 | 34 | | 湖北省 | 420000 | 35 | | 湖南省 | 430000 | 36 | | 广东省 | 440000 | 37 | | 广西壮族自治区 | 450000 | 38 | | 海南省 | 460000 | 39 | | 重庆市 | 500000 | 40 | | 四川省 | 510000 | 41 | | 贵州省 | 520000 | 42 | | 云南省 | 530000 | 43 | | 西藏自治区 | 540000 | 44 | | 陕西省 | 610000 | 45 | | 甘肃省 | 620000 | 46 | | 青海省 | 630000 | 47 | | 宁夏回族自治区 | 640000 | 48 | | 新疆维吾尔自治区 | 650000 | 49 | | 台湾省 | 710000 | 50 | | 香港特别行政区 | 810000 | 51 | | 澳门特别行政区 | 820000 | 52 | -------------------------------------------------------------------------------- /libs/enumApi.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * 返回枚举值 4 | * eg: getEnumKey(PhysicalCondition, 1)--- NORMAL 5 | * eg: getEnumKey(PhysicalCondition, 1, true)--- '正常' 6 | * params 7 | * nameCodeEnum 枚举名 8 | * valueEnum 需要查找的枚举值 9 | * */ 10 | function getEnumKey(nameCodeEnum, valueEnum, getName) { 11 | if (nameCodeEnum && valueEnum !== undefined) { 12 | if (typeof valueEnum === 'object') { 13 | valueEnum = valueEnum.value; 14 | } 15 | let enumKey = null; 16 | for (let key in nameCodeEnum) { 17 | if (!getName) { 18 | for (let k in nameCodeEnum[key]) { 19 | if (nameCodeEnum[key][k] === valueEnum) { 20 | enumKey = key; 21 | return enumKey; 22 | } 23 | } 24 | } else { 25 | if (nameCodeEnum[key].value === valueEnum) { 26 | return nameCodeEnum[key].name; 27 | } 28 | } 29 | } 30 | } 31 | } 32 | 33 | const AreaGrade = { 34 | COUNTRY: {name: '国家', value: 0}, 35 | PROVINCE: {name: '省级', value: 1}, 36 | CITY: {name: '市级', value: 2}, 37 | COUNTY: {name: '县级', value: 3}, 38 | STREET: {name: '乡级', value: 4}, 39 | VILLAGE: {name: '社村', value: 5} 40 | }; 41 | 42 | module.exports = { 43 | getEnumKey, 44 | AreaGrade 45 | } -------------------------------------------------------------------------------- /libs/util.js: -------------------------------------------------------------------------------- 1 | const BASE_URI = 'https://geo.datav.aliyun.com/areas_v2/bound/' 2 | const ROOTPATH = 'map/' 3 | const { getEnumKey, AreaGrade } = require('./enumApi') 4 | const request = require('request'); 5 | 6 | const fs = require('fs') 7 | 8 | function isProvinceId(id) { 9 | return Number.isInteger(id / 10000) && (id + '').length === 6; 10 | } 11 | 12 | function isCityId(id) { 13 | return Number.isInteger(id / 100) && (id + '').length === 6; 14 | } 15 | 16 | function isCountyId(id) { 17 | return (id + '').length === 6 && !isProvinceId(id) && !isCityId(id); 18 | } 19 | 20 | function isCountryId(id) { 21 | return (id + '').length !== 6; 22 | } 23 | 24 | function isStreetId(id) { 25 | return (id + '').length === 12 && Number.isInteger(id / 1000); 26 | } 27 | 28 | function isVillageId(id) { 29 | return (id + '').length === 12 && !isStreetId(id); 30 | } 31 | 32 | /** 33 | * 获取父级的id 34 | * @param id 35 | * @returns {string|*[]|number} 36 | */ 37 | function getParentId(id) { 38 | let str = id + ''; 39 | if (str.length === 6) { 40 | if (str.endsWith('0000')) { 41 | return 86; 42 | } else if (str.endsWith('00')) { 43 | return parseFloat(str.substr(0, 2) + '0000'); 44 | } else { 45 | return parseFloat(str.substr(0, 4) + '00'); 46 | } 47 | } else if (str.length > 6) { 48 | if (str.endsWith('000')) { 49 | return parseFloat(str.substr(0, 6)); 50 | } else { 51 | return parseFloat(str.substr(0, 9) + '000'); 52 | } 53 | } else { 54 | return 0; 55 | } 56 | } 57 | 58 | function getProvinceId(id) { 59 | if (id && id !== 86) { 60 | return (id + '').substring(0, 2) + '0000' 61 | } 62 | return id; 63 | } 64 | 65 | /** 66 | * 根据id获取行政等级 67 | * @param id 68 | * @returns {undefined} 69 | */ 70 | function getAreaGrade(id) { 71 | if (id) { 72 | if (isProvinceId(id)) { 73 | return getEnumKey(AreaGrade, AreaGrade.PROVINCE.value); 74 | } else if (isCityId(id)) { 75 | return getEnumKey(AreaGrade, AreaGrade.CITY.value); 76 | } else if (isStreetId(id)) { 77 | return getEnumKey(AreaGrade, AreaGrade.STREET.value); 78 | } else if (isVillageId(id)) { 79 | return getEnumKey(AreaGrade, AreaGrade.VILLAGE.value); 80 | } else if (isCountryId(id)) { 81 | return getEnumKey(AreaGrade, AreaGrade.COUNTRY.value); 82 | } else if (isCountyId(id)) { 83 | return getEnumKey(AreaGrade, AreaGrade.COUNTY.value); 84 | } 85 | } 86 | } 87 | 88 | function creatFileUri(areaId) { 89 | let areaKey = getAreaGrade(areaId); 90 | if (AreaGrade[areaKey].value > AreaGrade.CITY.value) { 91 | return `${BASE_URI}${areaId}.json` 92 | } else { 93 | return `${BASE_URI}${areaId}_full.json` 94 | } 95 | } 96 | 97 | function createDir(dirPath) { 98 | dirPath = ROOTPATH + dirPath + ''; 99 | let exist = fs.existsSync(dirPath) 100 | if (!exist) { 101 | fs.mkdirSync(dirPath); 102 | } 103 | return Promise.resolve(); 104 | } 105 | 106 | function sleep(ms) { 107 | return new Promise(resolve => setTimeout(resolve, ms)); 108 | } 109 | 110 | //从命令行中获取输入的areaid 111 | function getCliAreaId() { 112 | let areaid = process.argv.slice(2).pop() 113 | if (!areaid || areaid.length !=6) { 114 | console.log('请输入正确的区域id!') 115 | } else if (Number.isInteger(Number(areaid))) { 116 | targetAreaId = areaid; 117 | return targetAreaId; 118 | } else { 119 | console.error('参数错误!') 120 | } 121 | } 122 | 123 | function requestData(url) { 124 | return new Promise((resolve, reject) => { 125 | request(url,(err,res) => { 126 | if(err){ 127 | reject(err) 128 | }else{ 129 | try { 130 | resolve(JSON.parse(res.body)) 131 | } catch (error) { 132 | reject(error) 133 | } 134 | } 135 | }) 136 | }) 137 | } 138 | 139 | module.exports = { 140 | isProvinceId, 141 | isCountryId, 142 | getAreaGrade, 143 | getProvinceId, 144 | sleep, 145 | creatFileUri, 146 | createDir, 147 | BASE_URI, 148 | ROOTPATH, 149 | requestData, 150 | getCliAreaId 151 | } 152 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "GeoJsonFileCrawler", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "app.js", 6 | "dependencies": { 7 | "async": "^3.2.0", 8 | "request": "^2.88.2" 9 | }, 10 | "devDependencies": {}, 11 | "scripts": { 12 | "server": "node ./src/app.js", 13 | "area": "node ./src/getAreaGeoData.js" 14 | }, 15 | "keywords": [], 16 | "author": "", 17 | "license": "ISC" 18 | } 19 | -------------------------------------------------------------------------------- /src/app.js: -------------------------------------------------------------------------------- 1 | const { downLoadFile } = require('./downLoadFile'); 2 | const { parseFile } = require('./parseFile'); 3 | const async = require('async'); 4 | const {getCliAreaId} = require('../libs/util') 5 | 6 | let targetAreaId = 0;// 7 | 8 | let count = 0; 9 | 10 | async function downLoadAllArea(areaId) { 11 | let areaList = await downLoadAndParse(areaId); 12 | 13 | if (Array.isArray(areaList) && areaList.length > 1) { 14 | // Promise.all(areaList.map(async area => { 15 | // let res = await downLoadAndParse(area.areaId) 16 | // for (let i = 0; i < res.length; i++) { 17 | // const element = res[i]; 18 | // await downLoadAllArea(element.areaId) 19 | // } 20 | // })) 21 | async.mapLimit(areaList, 1, async area => { 22 | await downLoadAllArea(area.areaId) 23 | }) 24 | } else { 25 | count++; 26 | console.log(areaList[0].name + `(第${count}个)` + '下载完毕!'); 27 | } 28 | } 29 | 30 | 31 | async function downLoadAndParse(areaId) { 32 | await downLoadFile(areaId) 33 | let areaList = await parseFile(areaId) 34 | return areaList 35 | } 36 | 37 | 38 | (async () => { 39 | targetAreaId = getCliAreaId(); 40 | if (targetAreaId) { 41 | await downLoadAllArea(targetAreaId) 42 | } 43 | })() 44 | 45 | module.exports = { 46 | downLoadAndParse 47 | } -------------------------------------------------------------------------------- /src/downLoadFile.js: -------------------------------------------------------------------------------- 1 | 2 | const fs = require('fs'); 3 | const { getProvinceId, isCountryId } = require('../libs/util.js') 4 | const { creatFileUri,createDir,ROOTPATH} = require('../libs/util'); 5 | const request = require('request') 6 | async function downLoadFile(areaId) { 7 | // await sleep(1000) 8 | //https://geo.datav.aliyun.com/areas_v2/bound/140100_full.json 9 | /** 10 | * 省市地图文件的path都会带_full,区县的就不会带_full了 11 | */ 12 | const fileUri = creatFileUri(areaId); 13 | let filePath = ROOTPATH + (isCountryId(areaId) ? `./${areaId}.json` : `./${getProvinceId(areaId)}/${areaId}.json`) 14 | // if (isProvinceId(areaId)) { 15 | await createDir(getProvinceId(areaId)) 16 | // } 17 | var stream = fs.createWriteStream(filePath, { autoClose: true }); 18 | return new Promise((resolve, reject) => { 19 | request(fileUri).pipe(stream).on('close', () => { 20 | // console.log(`${areaId}下载成功!`) 21 | resolve(areaId); 22 | }).on('error', (err) => { 23 | // console.log(`${areaId}下载失败!`) 24 | reject(err); 25 | }); 26 | }) 27 | } 28 | 29 | module.exports = { 30 | downLoadFile 31 | } -------------------------------------------------------------------------------- /src/getAreaGeoData.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 获取被处理后的地区json数据 3 | */ 4 | 5 | const { creatFileUri, requestData, getCliAreaId } = require('../libs/util'); 6 | const parseGeoData = require('./parseGeoData'); 7 | 8 | async function getAreaGeoData() { 9 | let areaId = getCliAreaId(); 10 | let uri = creatFileUri(areaId) 11 | let res = await requestData(uri) 12 | return parseGeoData(res).filter(v => v.name) 13 | } 14 | 15 | module.exports = getAreaGeoData; 16 | 17 | (async() => { 18 | let res = await getAreaGeoData() 19 | console.log(res) 20 | })() -------------------------------------------------------------------------------- /src/getChinaAllArea.js: -------------------------------------------------------------------------------- 1 | const { downLoadAndParse } = require('./app.js') 2 | 3 | const areaId = 100000; 4 | 5 | (async () => { 6 | let res = await downLoadAndParse(areaId) 7 | console.log(res.filter(v=>v.name).length) 8 | console.log(res) 9 | })() -------------------------------------------------------------------------------- /src/parseFile.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs') 2 | const { getAreaGrade,getProvinceId,isCountryId } = require('../libs/util') 3 | const parseGeoData = require('./parseGeoData') 4 | const { AreaGrade } = require('../libs/enumApi') 5 | const ROOTPATH = 'map/' 6 | 7 | function readFile(filePath) { 8 | let exit = fs.existsSync(filePath); 9 | if (exit) { 10 | let fileBuffer = fs.readFileSync(filePath); 11 | let obj = JSON.parse(fileBuffer.toString()); 12 | return obj; 13 | } else { 14 | console.log(`文件${filePath}不存在`) 15 | return null 16 | } 17 | } 18 | 19 | function parseFile(areaId) { 20 | try { 21 | let filePath = ROOTPATH + (isCountryId(areaId)?`./${areaId}.json`:`./${getProvinceId(areaId)}/${areaId}.json`) 22 | let fileObj = readFile(filePath) 23 | let areaList = parseGeoData(fileObj) 24 | return Promise.resolve(areaList); 25 | } catch (err) { 26 | return Promise.reject(err); 27 | } 28 | } 29 | 30 | module.exports = { 31 | parseFile 32 | } 33 | -------------------------------------------------------------------------------- /src/parseGeoData.js: -------------------------------------------------------------------------------- 1 | const { AreaGrade } = require('../libs/enumApi') 2 | const { getAreaGrade } = require('../libs/util') 3 | 4 | module.exports = function (data) { 5 | if (data && data.features) { 6 | let features = data.features; 7 | let areaList = features.map(item => { 8 | let properties = item.properties 9 | return { 10 | name: properties.name, 11 | areaId: properties.adcode, 12 | areaGrade: AreaGrade[getAreaGrade(properties.adcode)] 13 | } 14 | }) 15 | return areaList; 16 | } else { 17 | return []; 18 | } 19 | } --------------------------------------------------------------------------------