获取百度上关于深圳市的全部POI数据。php
百度POI行业分类git
这个连接给出了百度的POI分类标准,包括17个一级类别,每一个一级类别下面有多个二级类别。github
此次实验咱们但愿按照一级类别分类来获取数据。web
Place APIjson
这个连接介绍了百度的POI接口。文档介绍的很详细,这里就不赘述了。可是有几个要点但愿读者注意:api
region
设置为省级行政区来得到每一个城市的POI数目coord_type
字段控制的是,参数的坐标系统,返回值的坐标系统是BD09LL
,可使用数值拟合的方法把该坐标系统的坐标转换到WGS84
由于一个请求最多只能返回400条数据,因此不可能使用城市内检索api
来获取整个城市的全部POI数据。一个可行的想法是,使用矩形检索API
。首先,咱们把城市等间距划分红多个矩形,使得每一个矩形都足够小以致其内部的POI数据条目少于400.而后在分别获取每一个矩形区域内的全部数据,就能够实现整个城市的POI数据获取。promise
这个方法虽然可行但存在一下缺点须要克服:网络
城市是不规则的多边形,简单的使用城市的bottomLeft和topRigh点来圈定城市的范围,将会包含不少城市外的区域。以深圳这样一个狭长的城市为例,它的外包矩形中大概包含了一半城市外区域。这致使咱们发起了不少没必要要的请求。
对于这个问题,咱们可使用Arcgis的fishnet工具
来生成矩形格网,而后使用select by location
,选出和城市相交的矩形。并发
POI数据的分布是很不均匀的,简单地均匀划分城市为多个矩形致使了,不少矩形内是没有POI数据的,而某些矩形内的POI数据仍然是远大于400的,没法彻底获取。
对于这个问题,咱们能够只对total == 400
的矩形进行进一步划分。async
请求数目过多,致使网络链接错误
// main const config = require('./config') const superagent = require('superagent') const co = require('co') const fs = require('fs') const transform = require('./transform') const parallel = require('co-parallel') const async = require('async') let type = process.argv[2] let outputFileName = `./${type}.csv` let writer = fs.createWriteStream(outputFileName) const coordinateSplit = require('./coordinateSplit') // 得到坐标块 let coordArr = coordinateSplit(100, config.bound.bottomLeft, config.bound.topRight) let qBase = { ak: config.ak, q: type, bounds: ``, output: 'json', coord_type: 1, page_size: 20, page_num: 0, } // 深圳 政府机构的poi数量 10827 function getPoiPromise(q) { return new Promise((resolve, reject) => { async.retry(5, (cb) => { superagent.get(config.url) .query(q) .timeout({ response: 500 }) .end((err, res) => { if (err) { cb(err) } else { cb(null, res) } }) }, (err, res) => { if (err) { resolve({err,q}) // 重连5次后,仍然错误, 也不要抛出错误,避免程序终止 } else { let obj = JSON.parse(res.text) resolve(obj) } }) }) } let numOfPoints = 0 let numOfQuery = 0 let numOfError = 0 function* thunnkGet(q) { return yield getPoiPromise(q) } co(function* () { let queryArr = [] let prePromiseArr = [] // 这个循环, 对每一个小块发起一个请求,来得到小块内的POI数目。 for (let i = 0; i < coordArr.length; i++) { let coord = coordArr[i] let q = Object.assign({}, qBase, { bounds: `${coord.bl.lat},${coord.bl.lng},${coord.tr.lat},${coord.tr.lng}`, page_num: 0 }) queryArr.push(q) prePromiseArr.push(thunnkGet(q)) } let preResArr = yield parallel(prePromiseArr, 80) for (let i = 0; i < preResArr.length; i++) { numOfPoints += preResArr[i].total } // 这个循环, 针对前一步得到的,块内POI数目,根据块内的POI数目,并发的发出多个请求,来得到具体的POI let promiseArr = [] for (let i = 0; i < queryArr.length; i++) { let q = queryArr[i] let total = preResArr[i].total if (total > 0) { let pageCount = Math.ceil(total / 20) console.log('页数:' + pageCount) for (let i = 0; i < pageCount; i++) { let pageQuery = Object.assign({}, q, { page_num: i }) // console.log(pageQuery) numOfQuery++ promiseArr.push(thunnkGet(pageQuery)) } } } let resArr = yield parallel(promiseArr, 100) for (let i = 0; i < resArr.length; i++) { let results = resArr[i].results if (!results) { numOfError++ console.log(resArr[i]) continue } console.log(`得到 ${results.length} 条`) for (let j = 0; j < results.length; j++) { let item = results[j] let wgsLnglat = transform.bd2wgs(item.location.lat, item.location.lng) writer.write(`${item.name},${wgsLnglat.lat},${wgsLnglat.lng},${item.address},${item.uid}\n`) } } }) .catch(err => { console.log(err) }) .then(() => { console.log('兴趣点数目:' + numOfPoints) console.log('请求数目:' + numOfQuery) console.log('错误数目:' + numOfError) }) // console.log(lngArr.length) // console.log(lngArr) // console.log(latArr.length) // console.log(latArr) // console.log(coordArr.length)
/** * 把一个由bottomLeft和topRight指定区域均匀划分为numOfCell块 * */ module.exports = function (numOfCell=100, bl, tr) { let numOfRowOrCoL = Math.sqrt(numOfCell) let spanLng = tr.lng - bl.lng let spanLat = tr.lat - bl.lat // 经度的步长 let stepLng = spanLng / numOfRowOrCoL // 纬度的步长 let stepLat = spanLat / numOfRowOrCoL let lngArr = [] let latArr = [] let beginLng = bl.lng let beginLat = bl.lat for (let i = 0; i < numOfRowOrCoL; i++) { lngArr.push(beginLng + i * stepLng) latArr.push(beginLat + i * stepLat) } lngArr.push(tr.lng) latArr.push(tr.lat) let coordArr = [] for (let row = 0; row < numOfRowOrCoL; row++) { for (let col = 0; col < numOfRowOrCoL; col++) { let bl = { lat: latArr[row], lng: lngArr[col], } let tr = { lat: latArr[row + 1], lng: lngArr[col + 1], } coordArr.push({ bl, tr, }) } } return coordArr }
参考文献: