獲取百度上關於深圳市的全部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 }
參考文獻: