推荐技术栈
- amap + g2/ amap + L7
- mapbox + deck.gl/echarts.gl
地理相关库
amap
mapbox
Leaflet
Cesium
deck.gl
g2 map类
turfjs
工具
http://geojson.io/#map=2/20.0...
地图选择器
地图3D
https://codepen.io/AlexZ33/pe...
https://codepen.io/AlexZ33/pe...
北京市居住人口3D分布
楼宇
https://codepen.io/AlexZ33/pe... 用css实现太累
城市统计
上海城市统计
图层
Mapv - 地理信息可视化开源库
https://github.com/chengquan2...
http://lbsyun.baidu.com/index...
http://mapv.baidu.com/gallery...
vue-baidu-map
注意
百度地图webapi接口文档
百度地图javascript api文档
使用百度地图的服务,需使用BD09坐标。
若使用非BD09坐标、未经过坐标转换(非BD09转成BD09)直接叠加在地图上,地图展示位置会偏移,因此通过其他坐标(WGS84、GCJ02)调用服务时,需先将其他坐标转换为BD09。
非百度坐标系,如何转换成百度坐标系?
http://lbsyun.baidu.com/index...
http://lbsyun.baidu.com/index...
图吧地图api
地图API和工具
坐标拾取器械
https://codepen.io/AlexZ33/pe...
另外,百度地图api的开发文档
下的工具支持
中有很多类似的工具
Vue Baidu Map
坐标系统说明
![图片描述 图片描述](https://image-static.segmentfault.com/299/556/2995569166-5a2632bfd96e2_articlex)
高德地图: GCJ-02 我国地图坐标系统
百度地图: BD-09 (BD-09II/bd09mc)
- 普通GPS定位出来的数值都是基于WSG-84坐标系标准,这是世界通用的坐标系。(美国的)
- GCJ-02和WSG-84之间的坐标系转换算法是保密的。
“中国政府为了国家安全在国内 GPS 定位时人为加入一定偏移”这种说法是不正确的。
应该是“我国所发行的地图类产品强制性加入偏移算法,使原本标准的坐标系统(WSG-84)变为国家保密的自定义坐标系统(GCJ-02)”。
坐标系说明
在进行地图开发过程中,我们一般能接触到以下三种类型的地图坐标系:
1.WGS-84原始坐标系,一般用国际GPS纪录仪记录下来的经纬度,通过GPS定位拿到的原始经纬度,Google和高德地图定位的的经纬度(国外)都是基于WGS-84坐标系的;但是在国内是不允许直接用WGS84坐标系标注的,必须经过加密后才能使用;
2.GCJ-02坐标系,又名“火星坐标系”,是我国国测局独创的坐标体系,由WGS-84加密而成,在国内,必须至少使用GCJ-02坐标系,或者使用在GCJ-02加密后再进行加密的坐标系,如百度坐标系。高德和Google在国内都是使用GCJ-02坐标系,可以说,GCJ-02是国内最广泛使用的坐标系;
3.百度坐标系:bd-09,百度坐标系是在GCJ-02坐标系的基础上再次加密偏移后形成的坐标系,只适用于百度地图。(目前百度API提供了从其它坐标系转换为百度坐标系的API,但却没有从百度坐标系转为其他坐标系的API)
three.js地图
http://blog.csdn.net/u0125393...
Three.js - 用100行javascript代码创建一座城市
G2地图
地图数据
通常情况下,地理数据的可视化会包含多份数据:一份是用于绘制地图的经纬度数据,一份是用户真正想要可视化的用户数据。
- 实例 中国地图-省市下钻只有经纬度数据,但是特殊的是,这个实例中,我们从amap api获得数据,在左侧绘制地图(其中中国地图直接得到的geoJSON数据,行政区划得到的是TopoJSON数据),在右侧用g2绘制处理行政区划数据(geojson -> json数组 --> dataset) 绘制地图
- 实例 带气泡的地图 需要在世界地图上标注各个国家的男女比例情况,这个时候就可以使用多视图的可视化方案:详情 戳 --->这
地图数据一般保存为JSON格式,G2和D3常用的有两种:
我们以这个g2 中国地图-省市下钻为例
g2/demos/map-drill-down.html
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>中国地图-省市下钻</title>
<link rel="stylesheet" href="http://cache.amap.com/lbs/static/main1119.css"/>
<style>
.button-group{
position: fixed;
bottom:50%;
left: 0px;
width: 70%;
}
</style>
<script type="text/javascript" src="http://cache.amap.com/lbs/static/addToolbar.js"></script>
</head>
<body>
<div id="mountNode"></div>
<div class='button-group' style="background-color: #fff">
<input type='radio' onclick='refresh(this.value)' checked name='mapStyle' value='normal'>标准
<input type='radio' onclick='refresh(this.value)' name='mapStyle' value='dark'>幻影黑
<input type='radio' onclick='refresh(this.value)' name='mapStyle' value='light'>月光银
<input type='radio' onclick='refresh(this.value)' name='mapStyle' value='fresh'>草色青
<input type='radio' onclick='refresh(this.value)' name='mapStyle' value='grey'>雅士灰<br>
<input type='radio' onclick='refresh(this.value)' name='mapStyle' value='graffiti'>涂鸦
<input type='radio' onclick='refresh(this.value)' name='mapStyle' value='whitesmoke'>远山黛
<input type='radio' onclick='refresh(this.value)' name='mapStyle' value='macaron'>马卡龙
<input type='radio' onclick='refresh(this.value)' name='mapStyle' value='blue'>靛青蓝
<input type='radio' onclick='refresh(this.value)' name='mapStyle' value='darkblue'>极夜蓝<br>
<input type='radio' onclick='refresh(this.value)' name='mapStyle' value='wine'>酱籽
</div>
<script>/*Fixing iframe window.innerHeight 0 issue in Safari*/document.body.clientHeight;</script>
<script src="https://gw.alipayobjects.com/os/antv/assets/g2/3.0.4-beta.2/g2.min.js"></script>
<script src="https://gw.alipayobjects.com/os/antv/assets/data-set/0.8.3/data-set.min.js"></script>
<script src="https://gw.alipayobjects.com/os/antv/assets/lib/jquery-3.2.1.min.js"></script>
<script src="https://gw.alipayobjects.com/os/antv/assets/lib/lodash-4.17.4.min.js"></script>
<script src="https://webapi.amap.com/maps?v=1.4.1&key=8c8c021990b332b22254f2f8289a62ef"></script>
<script src="https://webapi.amap.com/ui/1.0/main.js?v=1.0.11"></script>
<script>
$('#mountNode').html(
'<div style="position:relative;">'+
'<div id="china" style="width:50%;height:400px;position:absolute;left:0;top:0"></div>' +
'<div id="province" style="width:50%;height:400px;position:absolute;right:0;top:0;"></div>'+
'</div>'
);
//调用高德api绘制底图以及geo数据
const map = new AMap.Map('china',{
resizeEnable: true,
zoom:4
});
function refresh(enName) {
map.setMapStyle('amap://styles/'+enName);
}
const colors = [ "#3366cc", "#dc3912", "#ff9900", "#109618", "#990099", "#0099c6", "#dd4477", "#66aa00", "#b82e2e", "#316395", "#994499", "#22aa99", "#aaaa11", "#6633cc", "#e67300", "#8b0707", "#651067", "#329262", "#5574a6", "#3b3eac" ];
// 当前聚焦的区域
let currentAreaNode;
AMapUI.load(['ui/geo/DistrictExplorer', 'lib/$'], function(DistrictExplorer) {
//创建一个实例
const districtExplorer = window.districtExplorer = new DistrictExplorer({
eventSupport: true, //打开事件支持
map
});
// //创建一个辅助Marker,提示鼠标内容
// var tipMarker = new AMap.Marker({
// //启用冒泡,否则click事件会被marker自己拦截
// bubble:true
// });
//
// feature 被点击
districtExplorer.on('featureClick', function(e, feature) {
const props = feature.properties;
//如果存在子节点
console.log(props);
if(props.childrenNum > 0) {
//切换聚焦区域
switch2AreaNode(props.adcode);
}
});
//外部区域被点击
districtExplorer.on('outsideClick', function(e) {
districtExplorer.locatePosition(e.originalEvent.lnglat, function(error,routeFeatures) {
if(routeFeatures && routeFeatures.length > 1) {
//切换到省级区域
switch2AreaNode(routeFeatures[1].properties.adcode);
}else{
//切换到全国
switch2AreaNode(100000)
}
},{
levelLimit:2
});
});
//绘制某个区域的边界
function renderAreaPolygons(areaNode) {
const node = _.cloneDeep(areaNode);
districtExplorer.clearFeaturePolygons();
districtExplorer.renderSubFeatures(node, function(feature, i) {
const fillColor = colors[i % colors.length];
const strokeColor = colors[colors.length - 1 -i % colors.length];
return {
cursor: 'default',
bubble:true,
strokeColor,//线颜色
strokeOpacity:1,//线透明度
strokeWeight:1, //线宽
fillOpacity: 0.35 //填充透明度
};
});
//绘制父区域
districtExplorer.renderParentFeature(node, {
cursor: 'default',
bubble: true,
strokeColor: 'black',//线颜色
strokeOpacity: 1, //线透明度
strokeWeight: 1, //线宽
fillColor: null, //填充色
fillOpacity: 0.35 //填充透明度
});
}
//切换区域后刷新显示内容
function refreshAreaNode(areaNode) {
districtExplorer.setHoverFeature(null);
renderAreaPolygons(areaNode)
}
//切换区域
function switch2AreaNode(adcode, callback) {
if (currentAreaNode && ('' + currentAreaNode.getAdcode() === '' + adcode)) {
return;
}
loadAreaNode(adcode, function(error, areaNode) {
if (error) {
if (callback) {
callback(error);
}
return;
}
currentAreaNode = window.currentAreaNode = areaNode;
refreshAreaNode(areaNode);
if (callback) {
callback(null, areaNode);
}
});
}
//加载区域
function loadAreaNode(adcode, callback) {
districtExplorer.loadAreaNode(adcode, function(error, areaNode) {
if(error) {
if(callback) {
callback(error);
}
return;
}
renderG2Map(areaNode); //使用 G2 绘制地图
if(callback) {
callback(null, areaNode);
}
});
}
//浙江
switch2AreaNode(330000);
});
//开始使用G2绘制地图
let provinceChart;
function renderG2Map(areaNode) {
const adcode = areaNode.getAdcode();
const geoJSON = areaNode.getSubFeatures(); // 获取 geoJSON 数据
const name = areaNode.getName();
provinceChart && provinceChart.destroy();
provinceChart = null;
if (!geoJSON || currentAreaNode && ('' + currentAreaNode.getAdcode() === '' + adcode)) {
return;
}
const dv = processData(geoJSON);
// start: 计算地图的最佳宽高
const longitudeRange = dv.range('longitude');
const lantitudeRange = dv.range('lantitude');
const ratio = (longitudeRange[1] - longitudeRange[0]) / (lantitudeRange[1] - lantitudeRange[0]);
let width;
let height;
if (ratio > 1) {
width = $('#province').width();
height = width / ratio;
} else {
width = 300 * ratio;
height = $('#province').height();
}
// end: 计算地图的最佳宽高
provinceChart = new G2.Chart({
container: 'province',
width,
height,
padding: 0
});
provinceChart.source(dv);
provinceChart.axis(false);
provinceChart.tooltip({
showTitle: false
});
provinceChart
.polygon()
.position('longitude*lantitude')
.label('name', {
textStyle: {
fill: '#fff',
fontSize: 10,
shadowBlur: 2,
shadowColor: 'rgba(0, 0, 0, .45)'
}
})
.style({
stroke: '#fff',
lineWidth: 1
})
.color('value', '#BAE7FF-#1890FF-#0050B3');
provinceChart.guide().text({
position: [ 'min', 'max' ],
offsetY: 20,
content: name,
style: {
fontSize: 14,
fontWeight: 'bold'
}
});
provinceChart.render();
}
function processData(geoJSON) {
console.log("---------------------geoJSON---------------------------");
console.log(geoJSON);
const mapData = {
type: 'FeatureCollection',
features: geoJSON
};
// 构造虚拟数据
const userData = [];
for (let i = 0; i < geoJSON.length; i++) {
const name = geoJSON[i].properties.name;
userData.push({
name,
value: Math.round(Math.random() * 1000)
});
}
console.log("----------------userData----------------");
console.log(userData);
const ds = new DataSet();
const geoDataView = ds.createView().source(mapData, {
type: 'GeoJSON'
}); // geoJSON 经纬度数据
// 用户数据
const dvData = ds.createView().source(userData);
dvData.transform({
type: 'geo.region',
field: 'name',
geoDataView,
as: [ 'longitude', 'lantitude' ]
});
console.log('---------------------dvData-------------');
console.log(dvData);
return dvData;
}
</script>
</body>
</html>
这里的代码
AMapUI.load(['ui/geo/DistrictExplorer', 'lib/$'], function(DistrictExplorer) {
}
是对高德地图ui的组件调用,DistrictExplorer是行政区划浏览
- 自有组件还有:参考组件 UI组件库
- 我们还可以自定义组件模块,AMapUI组件库的扩展 ,详情戳这里————> AMapUI组件库高级功能
-
ui/geo/DistrictExplorer
是扩展的模块路径
- lib/$, 即DomLibrary(jQuery或者Zepto)
![图片描述 图片描述](https://image-static.segmentfault.com/847/450/847450985-5a5c51d3e6978_articlex)
这里load的json数据是这样的,其中的topo内的就是TopoJSON数据
geo/DistrictExplorer.json
![图片描述 图片描述](https://image-static.segmentfault.com/610/595/610595924-5a57002d60616_articlex)
- type 是Topology,表示文件类型
- transform用于描述缩放量和平移量,分别用一个只有两项的数组来表示
-
objects里存有几何体模块,此处只有浙江省
- parent 是全省
- sub 是各市县
- arcs表示如何从最外层的数组arcs里提取地形.
注意
:
相比GeoJSON直接使用Polygon、Point之类的几何体来表示图形的方法,TopoJSON中的每一个几何体都是通过将共享边(被称为arcs)
整合后组成
TopoJSON消除了冗余,文件大小缩小了80%,因为:
地图投影
我们再看一看 g2的另一个实例 : 带气泡的地图
const dv = ds.createView('back')
.source(mapData,{
type: 'GeoJSON'
})
.transform({
type:'geo.projection',
projection: 'geoMercator',
as:['x','y','centroidX','centroidY']
});
这里的 projection: 'geoMecator'
,表示投影是墨卡托投影
墨卡托投影、高斯-克吕格投影、UTM投影
我们可以看看d3里面关于地图的投影api,看看有哪些地图投影:
d3.js Azimuthal Projections
绘制过程
![图片描述 图片描述](https://image-static.segmentfault.com/380/977/3809770135-5a5c16cb7e082_articlex)
![图片描述 图片描述](https://image-static.segmentfault.com/174/150/1741504625-5a5c41a1279b3_articlex)
相比2.0版本 3.0版本的container是支持string 也支持dom对象的
从上面两个例子我们可以看出来,在载入地图时候我们可以
- 调用amap api地图
- 本地GeoJSON或TopoJSON生成
这里我们说一说如何载入数据
如何装载数据
![图片描述 图片描述](https://image-static.segmentfault.com/400/911/4009113128-5a5c1a1ba50dc_articlex)
![图片描述 图片描述](https://image-static.segmentfault.com/155/456/1554563613-5a5c53567376b_articlex)
这里我们可以看到 g2中绘制地图时候需要 传入一个JSON数组
所以上面例子中国地图-省市下钻 的 const dv = processData(geoJSON);
processData函数应该有这样一个转换过程 GeoJSON --> JSON数组 --> DataSet
,我们来看一看是不是这样(即userData应该是JSON数组,dvData应该是DataSet)
![图片描述 图片描述](https://image-static.segmentfault.com/823/961/823961638-5a5c5577e3b10_articlex)
![图片描述 图片描述](https://image-static.segmentfault.com/275/699/2756992183-5a5c4cb700a13_articlex)
![图片描述 图片描述](https://image-static.segmentfault.com/358/450/3584504975-5a5c4caa77262_articlex)
const geoDataView = ds.createView().source(mapData, {
type: 'GeoJSON'
}); // geoJSON 经纬度数据
为什么是转换为Dataset?
自 G2 3.0 版本开始,原先内置的数据处理模块 frame 从 G2 包中抽离出来,独立成为 DataSet 包。DataSet 的目标是为数据可视化场景提供状态驱动(state driven)的 -->DataSet
<script src="https://gw.alipayobjects.com/os/antv/assets/data-set/0.8.1/data-set.js"></script>
在DataSet包中
- 我们把数据处理分为两个大的步骤:数据连接(Connector)和数据转换(Transform)。Connector 负责导入和归一化数据(譬如导入 CSV 数据,导入 GeoJSON 数据等),Transform 负责进行各种数据转换操作(譬如图布局、数据统计、数据补全等)。
想要了解G2中数据的处理流程直接点击
常用实例
基于baidu、google、arcgis、高德地图、canvas数据可视化
GeoHey gallery
文章
基于WebGL的大数据二三维可视化--uber的deck.gl介绍
deck.gl example
可视化篇:mapbox + echarts-gl 展示血脉交通
从Mapbox的开源工具看Web GIS的发展
ECharts 地图博客
echarts结合高德API进行地图下钻
参考
Map Projection Overview
[python处理地理数据-geopandas和pyshp]()
mapbox/node-fontnik工具使用介绍
Mapbox GL JS本地化实践
GIS文章集
Geomatics(GIS,GPS,RS,Surveying)
地图应用API(GIS+LBS)
antv g2的理解总结
Maps
inMap
https://codepen.io/stevepeppl...
https://codepen.io/AlexZ33/pe...
https://codepen.io/pbeshai/pr...
https://codepen.io/jakealbaug...
https://github.com/LylaYuKako...