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表示缩放层级,瓦片编号规则如下图所示:
谷歌xyz

使用的地图商:
高德地图、谷歌地图、OpenStreetMap

百度xyz

瓦片坐标的原点在本初子午线和赤道的交汇处(经度0纬度0),Z从1开始,在最高级就把地图分为四块瓦片,瓦片编号规则如下图所示:
百度xyz

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

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐