何大小成

地图 · 轮廓数据 · geojson

最近刚开始在着手一个南沙区的可视化项目,希望在地图上显示3D建筑,而且还支持自定义建筑。当时看了百度Echarts的几个例子,发现实现这个功能有好几个方法,而我就选择了其中之一:

  1. 从高德地图爬虫获取建筑轮廓,生成geojson文件
  2. mapbox定制自定义地图
  3. echarts-gl生成

我认为关键一步还是如何获取数据,实现了这一步基本上都解决了渲染问题。

本文着重讲解如何从高德地图爬虫获取建筑轮廓,生成geojson文件

map

geojson

GeoJson是用于描述地理空间信息的数据格式。它不是一种新的数据格式,只是语法规则符合JSON格式,专门用于表示地理信息。(具体这里就不做详细讲解)

geojson.io可以手动制作GeoJson文件。利用网站给我们提供的工具可以自由绘制轮廓。绘制完给property填上height属性,在echarts就能知道建筑物高度.

map

但是手动绘制建筑轮廓和高度比较烦,另外还要知道经纬度。所以才采用从高德地图爬虫。

高德地图爬建筑轮廓

爬虫不要经常去尝试与测试,小心被封IP

思路就是:通过搜索,将接口返回的数据然后拼成geojson格式,然后写入文件即可。

  1. 搜索
    搜索意思是在网页搜索框输入地方,建筑范围比较大有轮廓数据,一些小吃店只有坐标,而不存在轮廓数据。以星河丹堤小区为例,蓝色部分就是我们想要的轮廓。

接口(poiInfo)返回的数据格式就是:

我们是程序员,不可能收集数据都用这种方法来只做geojson文件,所以最好自己写一段node程序来实现,实现获取南沙区的小区,公园等轮廓数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
var request = require('request')
var fs = require('fs')
let arr = []
let arrRoad = []
// 搜索的范围
let search = [
'小区',
'公园',
'医院',
'广场',
'写字楼'
]
// 搜索的地区
let district = '南沙区 '
let LENGTH = 0.001
let url = 'http://ditu.amap.com/service/poiInfo?query_type=TQUERY&pagesize=20&pagenum=1&qii=true&cluster_state=5&need_utd=true&utd_sceneid=1000&div=PC1000&addr_poi_merge=true&is_classify=true&zoom=17&city=440100&geoobj=113.605673%7C22.748379%7C113.621122%7C22.752178&keywords='
search.forEach(keyword => {
arr.push(new Promise(resolve => {
request((url + encodeURI(district + keyword)), (error, response, body) => {
if (!error && response.statusCode === 200) {
console.log(body)
let transBody = body && JSON.parse(body)
let {data} = transBody
let {poi_list} = data
// 轮廓规则:
// 1. 如果有轮廓数据,则按返回的数据为准
// 2. 如果没有轮廓数据,则以经纬度为中心,生成一个正方形
let regionData = poi_list.map(item => {
let hasAoi = item.domain_list.find(i => i.name === 'aoi')
let po1 = [
Number(item.longitude) + LENGTH,
item.latitude
]
let po2 = [
item.longitude,
Number(item.latitude) + LENGTH
]
let po3 = [
Number(item.longitude) - LENGTH,
item.latitude
]
let po4 = [
item.longitude,
Number(item.latitude) - LENGTH
]
return {
name: item.name,
coordinates: hasAoi.hasOwnProperty('value')
? [hasAoi.value.split('_').map(i => i.split(','))]
: [
[po1, po2, po3, po4, po1]
]
}
})
// 按照Geojson,高度先固定为10
let result = regionData.map(item => {
return {
'type': 'Feature',
'properties': {
'stroke': '#555555',
'stroke-width': 2,
'stroke-opacity': 1,
'fill': '#555555',
'fill-opacity': 0.5,
'height': 10,
'name': item.name
},
'geometry': {
'type': 'Polygon',
'coordinates': item.coordinates
}
}
})
resolve(result)
}
})
}))
})
Promise.all(arr).then(urls => {
let geoJSON = {}
geoJSON.type = 'FeatureCollection'
geoJSON.features = [].concat(...urls)
// 写入map.geojson文件
fs.unlink(__dirname + '/map.geojson', err => {
if (err) {
console.log(err)
return false
}
fs.writeFile(__dirname + '/map.geojson', JSON.stringify(geoJSON), {
flag: 'a'
}, err => {
if (err) {
console.error(err)
} else {
console.log('写入成功')
}
})
})
})