【MapBox实战】生成地图+绘制区域+纠偏
mapbox - 生成地图、绘制一块区域、上点方法、瓦片地图原理、纠偏原理和实验
【MapBox实战】生成地图+绘制区域+纠偏
mapbox介绍
mapbox-gl.js - leaflet的插件
前端渲染矢量瓦片交互地图的工具
mapbox-gl.js必须要支持WebGL(旧浏览器不支持)
GIS需要处理的两部分:数据来源+数据在界面显示的样子,style中的source、layer对应这两个部分
生成地图过程
首先是注册登录,银行卡号的部分使用了随机生成器,邮编随便填一个,可能会报错但依然会发送确认邮件,确认后可以成功注册。
随机生成器网址:银行卡随机生成器
1、头部引入mapbox
<script src='https://api.mapbox.com/mapbox-gl-js/v2.9.1/mapbox-gl.js'></script>
<link href='https://api.mapbox.com/mapbox-gl-js/v2.9.1/mapbox-gl.css' rel='stylesheet' />
2、定义一个容器来显示地图
<div id='map' style='width: 100%; height: 100%;'></div>
3、script里设置map对象和相关参数
<script>
mapboxgl.accessToken = '这里填官网获取的token';
var map = new mapboxgl.Map({
container: 'map',
style: 'mapbox://styles/mapbox/streets-v11',
center: [118.38, 31.33],//芜湖市的经纬度(可以填你想要显示的城市经纬度)
zoom: 9
});
此时可以显示芜湖为中心的地图了
在头部引入mapbox-gl-language.js,将地图上显示的文字转换为中文
<script src='https://api.mapbox.com/mapbox-gl-js/plugins/mapbox-gl-language/v1.0.0/mapbox-gl-language.js'></script>
在srcipt内部设置:
mapboxgl.setRTLTextPlugin('https://api.mapbox.com/mapbox-gl-js/plugins/mapbox-gl-rtl-text/v0.1.0/mapbox-gl-rtl-text.js');
map.addControl(new MapboxLanguage({defaultLanguage: "zh-Hans"}));
基础配置
坐标
数组形式[A,B],A是经度,B是维度
在地图上绘制一块区域
1、获取想显示的区域json数据:进入阿里云数据可视化平台获取DataV.GeoAtlas地理小工具系列 (aliyun.com)
在jiujiang.js文件中定义变量
var jiujiangJSON = {...}//此处放从网站上复制下来的json数据
2、在头部引入区域的js文件
<script src="./jiujiang.js"></script>
3、在地图加载后,增加图层,在这个图层上绘制type='fill'
也就是区域,在'paint'
里设置绘制的区域样式
map.on('load', function () {
map.addLayer({
'id': 'jiujiang',
'type': 'fill',
'source': {
'type': 'geojson',
'data': jiujiangJSON
},
'layout': {},
'paint': {
'fill-color': '#088',
'fill-opacity': 0.4,
}
});
});
其中:
type - 放的是绘制的类型,此处是fill,如果要绘制线条,就设置为line
source - 其中的data放json数据,由于头部引入了js文件,直接使用js文件里的变量名jiujiang JSON来获取json数据
paint - 在这里配置绘图的样式,比如区域颜色,透明度等,这里设置的属性应该和type对应,可以去查看开发文档,有详细说明。或者看此链接地图样式有详细整理
4、如果希望区域的边界线条更明显,则需要另外实现一个图层来绘制线条,因为fill相关属性没有设置线条宽度的属性
map.addLayer({
'id': 'jiujiangline',
'type': 'line',
'source': {
'type': 'geojson',
'data': jiujiangJSON
},
'layout': {},
'paint': {
'line-color': 'blue',
'line-width': 3
}
});
效果如下:
在地图上画上点
1、获取这个区域的一些点坐标,放入json文件
var pointsJSON = {
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [118.176944, 31.18]
},
},
{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [118.103611, 31.261944]
},
},
{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [118.039654, 31.19909]
},
},
{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [118.0209734, 31.282277]
},
},
{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [118.109444, 31.284444]
},
}
]
}
2、由于要使用自定义的点图标,因此使用loadImage方法,加载图标
*注意:如果使用vscode练习,会出现本地路径访问异常的问题,下载一个Live Server的插件,并点击右下角GoLive可以解决问题。
map.loadImage('./site.png', function(error, image) {
if (error) throw error;
map.addImage('pointImg', image);
map.addLayer({
"id": "points",
"type": "symbol",
"source": {
"type": "geojson",
"data": pointsJSON
},
"layout": {
"icon-image": "pointImg",
"icon-size": 0.1
}
});
});
data里的数据和区域部分处理方式相同,icon-size设置图标在地图上的显示大小,效果如下:
瓦片地图原理理解
原理
正确加载主要是依托经纬度和瓦片的互转算法
瓦片地图背景理解
瓦片分层理解:
图上分了三层,可以想象最开始地图展示的中国地图是最上层,只需要一张瓦片(256px*256px);接着对地图进行放大,页面展示出了某省地图,此时信息量变多,像素变高,需要很多张瓦片显示。
编码方式
谷歌xyz
瓦片的坐标原点在世界地图的左上角,西经180 º北纬85 º左右,Z表示缩放层级,瓦片编号规则如下图所示:
使用的地图商:
高德地图、谷歌地图、OpenStreetMap
百度xyz
瓦片坐标的原点在本初子午线和赤道的交汇处(经度0纬度0),Z从1开始,在最高级就把地图分为四块瓦片,瓦片编号规则如下图所示:
mapbpx-gl纠偏
mapbox的地图资源基于OSM,其坐标系使用的是WGS84
高德地图和腾讯地图坐标系是GCJ02
坐标系转换方法
- 方法定义
const {PI} = Math
// 球体长半径
const SPHERE_RADIUS = 6378245.0
// 扁率
const FLATNESS = 0.00669342162296594323
const ER = 20037508.342789
function transformLat(inputLng, inputLat) {
const lat = +inputLat
const lng = +inputLng
let ret = -100.0 + 2.0 * lng + 3.0 * lat + 0.2 * lat * lat + 0.1 * lng * lat + 0.2 * Math.sqrt(Math.abs(lng))
ret += (20.0 * Math.sin(6.0 * lng * PI) + 20.0 * Math.sin(2.0 * lng * PI)) * 2.0 / 3.0
ret += (20.0 * Math.sin(lat * PI) + 40.0 * Math.sin(lat / 3.0 * PI)) * 2.0 / 3.0
ret += (160.0 * Math.sin(lat / 12.0 * PI) + 320 * Math.sin(lat * PI / 30.0)) * 2.0 / 3.0
return ret
}
function transformLng(inputLng, inputLat) {
const lat = +inputLat
const lng = +inputLng
let ret = 300.0 + lng + 2.0 * lat + 0.1 * lng * lng + 0.1 * lng * lat + 0.1 * Math.sqrt(Math.abs(lng))
ret += (20.0 * Math.sin(6.0 * lng * PI) + 20.0 * Math.sin(2.0 * lng * PI)) * 2.0 / 3.0
ret += (20.0 * Math.sin(lng * PI) + 40.0 * Math.sin(lng / 3.0 * PI)) * 2.0 / 3.0
ret += (150.0 * Math.sin(lng / 12.0 * PI) + 300.0 * Math.sin(lng / 30.0 * PI)) * 2.0 / 3.0
return ret
}
/**
* 判断是否在国内,不在国内则不做偏移
* @param lng
* @param lat
* @returns {boolean}
*/
function outOfChina(inputLng, inputLat) {
const lat = +inputLat
const lng = +inputLng
// 纬度 3.86~53.55, 经度 73.66~135.05
return !(lng > 73.66 && lng < 135.05 && lat > 3.86 && lat < 53.55)
}
- wgs84 转 gcj02
function wgs84ToGcj02(inputLng, inputLat) {
const lat = +inputLat
const lng = +inputLng
if (outOfChina(lng, lat)) {
return [lng, lat]
} else {
let dLat = transformLat(lng - 105.0, lat - 35.0)
let dLng = transformLng(lng - 105.0, lat - 35.0)
const radLat = lat / 180.0 * PI
let magic = Math.sin(radLat)
magic = 1 - FLATNESS * magic * magic
const sqrtMagic = Math.sqrt(magic)
dLat = (dLat * 180.0) / ((SPHERE_RADIUS * (1 - FLATNESS)) / (magic * sqrtMagic) * PI)
dLng = (dLng * 180.0) / (SPHERE_RADIUS / sqrtMagic * Math.cos(radLat) * PI)
const mgLat = lat + dLat
const mgLng = lng + dLng
return [mgLng, mgLat]
}
}
- gcj02 转 wgs84
function gcj02towgs84(lng, lat){
if (outOfChina(lng, lat)){
return [lng, lat]
}else {
dlat = transformLat(lng - 105.0, lat - 35.0)
dlng = transformLng(lng - 105.0, lat - 35.0)
radlat = lat / 180.0 * PI
magic = Math.sin(radlat)
magic = 1 - FLATNESS * magic * magic
sqrtmagic = Math.sqrt(magic)
dlat = (dlat * 180.0) / ((SPHERE_RADIUS * (1 - FLATNESS)) / (magic * sqrtmagic) * PI)
dlng = (dlng * 180.0) / (SPHERE_RADIUS / sqrtmagic * Math.cos(radlat) * PI)
mglat = lat + dlat
mglng = lng + dlng
return [lng * 2 - mglng, lat * 2 - mglat]
}
}
学习整合高德地图的方法
1、尝试在mapbox上再加载高德API(更美观,道路等更全)
//头部加上
<!-- 加载地图JSAPI脚本 -->
<script src="https://webapi.amap.com/maps?v=2.0&key=096ec74ca9fc2c8f7958c064fc918827"></script>
2、设置全局变量,确定中心和图层
const center = [118.450393,31.341593]
const zoom = 11
3、初始化地图
const amapMap = new AMap.Map('amap', {
viewMode: '3D',
pitch: 0,
zoom: zoom + 1, //初始化地图层级
center: aMapCenter //初始化地图中心点
})
4、由于center此时的经纬度是WGS84下的,因此需要通过转换的方法,转换成GJC02,这样高德地图的中心就可以定在正确的位置
const aMapCenter = wgs84ToGcj02(center[0],center[1])
5、监听mapbox的move和zoom事件(也就是拖动和放大缩小)
mapboxMap.on('move', () => {
linkAMap()
})
mapboxMap.on('zoom', () => {
linkAMap()
});
6、linkAMap方法,就是让地图的中心变化,setZoom和setCenter方法中第二个参数定为true,作用是地图不是缓慢变化的,不会呈现移动的效果
function linkAMap(){
const mapboxZoom = mapboxMap.getZoom() + 1
const mapboxCenter = mapboxMap.getCenter()
amapMap.setZoom(mapboxZoom,true)
amapMap.setCenter(wgs84ToGcj02(mapboxCenter.lng,mapboxCenter.lat),true)
}
其中调用了坐标系转换方法
同步去通过转换让高德的定位和mapbox重合,此时两个容器就重叠了
此时上点,由于是以WGS84为坐标系,因此坐标还应是wgs84下坐标可以看到成功显示,位置正确:
学习使用纠偏插件
结果也显示正确
插件介绍地址:mapboxgl 纠偏百度地图 - 知乎 (zhihu.com)
效果:
此原理和整合不同,是加载每张瓦片时进行一个对应
最后 + 实例仓库地址
此文主要是我对于mapbox的学习和实验,在实践中加强对地图知识的认知,其中借鉴了许多优秀的文章,希望能帮助到有需要的人,如有问题欢迎交流~
个人demo链接,可供参考:mapbox本文demo
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)