mapbox自定义绘制工具
如何使用mapbox绘制常用的工具
许多小伙伴刚接触map的时候,都不知道怎么下手,都是一味埋头网上一顿搜。然后发现别人写的东西自己发现拿过来放在自己的代码中不合适。有些怎么实例化地图都不知道,所以看官网API是非常重要的,一来让你对mapbox一些属性,方法以及事件有一个初步的了解,然后在结合别人写的功能自己才会容易上手。操作地图不外乎就是一些事件,其实mapbox有一些关于绘制工具的一些插件,也可以自己定义绘制工具。
我这里的demo,就是一个很简单的实现。实现(point,line,polygon,circle,rectangle)我用到的事件就三种(click(鼠标单击),mousemove(鼠标移动),dblclick(双击结束),就根据这三个事件做这几个绘制工具。希望对你在网上找关于mapbox绘制工具在逻辑上有一个直观的了解,为了方便参考者一看就懂。所以我把各个功能的代码分开展示。如果想要看本实例效果的自己创建一个html文件把一下代码顺序复制到你所创建的html文件中。直接打开即可,当然这里面的demo可能不完美,参考者可以根据需要自行修改。
需要引入的资源
<script src='https://api.mapbox.com/mapbox-gl-js/v2.0.0/mapbox-gl.js'></script>
<script src="https://cdn.bootcss.com/Turf.js/5.1.6/turf.js"></script>
<script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
Turf.js用于计算面积,距离使用,
创建div
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>mapbox测量工具</title>
<link href='https://api.mapbox.com/mapbox-gl-js/v2.0.0/mapbox-gl.css' rel='stylesheet' />
<style>
body { margin:0; padding:0; }
#map { position:absolute; top:0; bottom:0; width:100%; }
.map-legend{
position:absolute;
z-index: 5;
}
.measure-result {
background-color: white;
border-radius: 3px;
height: 16px;
line-height: 16px;
padding: 0 3px;
font-size: 12px;
box-shadow: 0 0 0 1px #ccc;
position: absolute;
}
.measure-result .close {
cursor: pointer;
width: 14px;
height: 14px;
line-height: 16px;
text-align: center;
padding: 0;
}
</style>
</head>
<body>
<div id='map'>
<div class="map-legend">
<button id="circle">圆</button>
<button id="point">点</button>
<button id="line">线</button>
<button id="polygon">多边形</button>
<button id="rectangle">矩形</button>
</div>
</div>
</body>
引入mapbox实例
mapboxgl.accessToken = 'pk.eyJ1IjoibGlhbmJvIiwiYSI6ImNrYWRqeXN5ZzAwenYycm9nZ2M5ZDIzMzMifQ.jTm1afxWBB-OxKGp57wjaw';
var map = new mapboxgl.Map({
container: 'map',
maxZoom: 18,
minZoom: 0,
zoom: 10,
center: [109.1699, 45.9719],
style: 'mapbox://styles/mapbox/streets-v11',
attributionControl: false
});
设置circle工具
//绘制圆形区域的函数
let createGeoJSONCircle = function(center, radiusInKm, points) {
if(!points) points = 64;
var coords = {
latitude: center[1],
longitude: center[0]
};
var km = radiusInKm;
var ret = [];
var distanceX = km/(111.320*Math.cos(coords.latitude*Math.PI/180));
var distanceY = km/110.574;
var theta, x, y;
for(var i=0; i<points; i++) {
theta = (i/points)*(2*Math.PI);
x = distanceX*Math.cos(theta);
y = distanceY*Math.sin(theta);
ret.push([coords.longitude+x, coords.latitude+y]);
}
ret.push(ret[0]);
// {
// "type": "geojson",
// "data": {
// "type": "FeatureCollection",
// "features": [{
// "type": "Feature",
// "geometry": {
// "type": "Polygon",
// "coordinates": [ret]
// }
// }]
// }
// };
return {
"type": "Feature",
"geometry": {
"type": "Polygon",
"coordinates": [ret]
}
};
};
$("#circle").click(function (e) {
debugger
map.getCanvas().style.cursor = 'crosshair';
e.stopPropagation();
clearLayerAndSource();
var isDraws = true;
// 禁止双击缩放
map.doubleClickZoom.disable();
var radius=0;
var jsonCircle = {
'type': 'FeatureCollection',
'features': []
};
let _pixelRadius=0;
var source = map.getSource('circle');
if(source){
//map.getSource('circle').setData(jsonCircle);
}else{
map.addSource('circle', {
type: 'geojson',
data: jsonCircle
});
map.addLayer({
"id": "circle",
"type": "fill",
"source": "circle",
"layout": {
},
"paint": {
"fill-color": "#e6205e",
"fill-opacity": 0.6,
//"circle-radius": {
// stops: [
// [0, 0],
// [20, _pixelRadius]],
// base: 2
// },
// "circle-opacity": 0.5,
// "circle-stroke-width": 1,
// "circle-color": "#00f",
// "circle-pitch-alignment": "map"
}
});
}
var points = [];
var starCoords=[];
let isMousemove=false;
let isFilst=true;
let filstCoords=[];
map.on('click', function (_e) {
if(isDraws){
starCoords=[];
starCoords.push(_e.lngLat.lat);
starCoords.unshift(_e.lngLat.lng);
if(isFilst){
filstCoords=starCoords;
isFilst=false
}
points.push(starCoords);
map.getSource('circle').setData(createGeoJSONCircle(starCoords, 0));
isMousemove=true;
isDraws=true;
}
})
var moveCoords=[]
map.on('mousemove', function (_e) {
if(isDraws && isMousemove){
isDraws=true;
var centerCoords=[]
moveCoords=[_e.lngLat.lng, _e.lngLat.lat];
if(filstCoords.length !=0){
centerCoords=[(parseFloat(filstCoords[0])+parseFloat(moveCoords[0]))/2,(parseFloat(filstCoords[1])+parseFloat(moveCoords[1]))/2];
var _points = [];
_points.push(moveCoords)
_points.unshift(filstCoords);
//points.concat([moveCoords]);
var line = turf.lineString(_points);
var len = turf.length(line);
if(len < 1) {
_pixelRadius=len
//m
// len = Math.round(len * 1000);
// map.getSource('circle').setData(createGeoJSONCircle(filstCoords, len));
} else {
//km
len = len.toFixed(2);
_pixelRadius=len
map.getSource('circle').setData(createGeoJSONCircle(filstCoords, len));
}
}
}
})
map.on('dblclick', function (_e) {
if(isDraws){
map.getCanvas().style.cursor = 'grab'
jsonCircle = {
'type': 'FeatureCollection',
'features': []
};
//map.getSource('circle').setData(jsonCircle);
isMousemove=false
isDraws=false;
map.getCanvas().style.cursor = '';
var endCoords=[_e.lngLat.lng, _e.lngLat.lat];
if(filstCoords.length !=0){
debugger
//var _points = points.concat([endCoords]);
var _points = [];
_points.push(moveCoords)
_points.unshift(filstCoords);
var line = turf.lineString(_points);
var len = turf.length(line);
if(len < 1) {
_pixelRadius=len
// len = Math.round(len * 1000);
// map.getSource('circle').setData(createGeoJSONCircle(filstCoords, len));
}else{
len = len.toFixed(2);
_pixelRadius=len
map.getSource('circle').setData(createGeoJSONCircle(filstCoords, len));
}
}
}
})
})
创建 point 绘制工具
$("#point").click(function (e) {
map.getCanvas().style.cursor = 'crosshair';
e.stopPropagation();
clearLayerAndSource();
var isDraw = true;
// 禁止双击缩放
map.doubleClickZoom.disable();
var jsonPoint = {
'type': 'FeatureCollection',
'features': []
};
var source = map.getSource('points');
if(source) {
map.getSource('points').setData(jsonPoint);
}else{
map.addSource('points', {
type: 'geojson',
data: jsonPoint
});
map.addLayer({
id: 'points',
type: 'circle',
source: 'points',
paint: {
'circle-color': '#ffffff',
'circle-radius': 3,
'circle-stroke-width': 2,
'circle-stroke-color': '#ff0000'
}
});
}
var points = [];
var starCoords=[];
map.on('click', function (_e) {
if (isDraw) {
starCoords=[];
starCoords.push(_e.lngLat.lat);
starCoords.unshift(_e.lngLat.lng);
points.push(starCoords);
}
})
map.on('dblclick', function (_e) {
if (isDraw) {
map.getCanvas().style.cursor = 'grab'
isDraw=false;
var endCoords=[_e.lngLat.lng, _e.lngLat.lat];
jsonPoint.features.push({
type: 'Feature',
geometry: {
type: 'Point',
coordinates: endCoords
}
});
map.getSource('points').setData(jsonPoint);
}
})
})
创建line绘制工具
$('#line').click(function (e) {
debugger
map.getCanvas().style.cursor = 'crosshair';
e.stopPropagation();
clearLayerAndSource();
var isMeasure = true;
// 禁止双击缩放
map.doubleClickZoom.disable();
var jsonPoint = {
'type': 'FeatureCollection',
'features': []
};
var jsonLine = {
'type': 'FeatureCollection',
'features': []
};
var markers = [];
var source = map.getSource('points');
var lineMoveSource = map.getSource('line-move');
var lineSource = map.getSource('line');
if(source) {
map.getSource('points').setData(jsonPoint);
if(lineMoveSource){
map.getSource('line-move').setData(jsonLine);
}
if(lineSource){
map.getSource('line').setData(jsonLine);
}
} else {
map.addSource('points', {
type: 'geojson',
data: jsonPoint
});
map.addSource('line', {
type: 'geojson',
data: jsonLine
});
map.addSource('line-move', {
type: 'geojson',
data: jsonLine
});
map.addLayer({
id: 'line-move',
type: 'line',
source: 'line-move',
paint: {
'line-color': '#ff0000',
'line-width': 2,
'line-opacity': 0.65
}
});
map.addLayer({
id: 'line',
type: 'line',
source: 'line',
paint: {
'line-color': '#ff0000',
'line-width': 2,
'line-opacity': 0.65
}
});
map.addLayer({
id: 'points',
type: 'circle',
source: 'points',
paint: {
'circle-color': '#ffffff',
'circle-radius': 3,
'circle-stroke-width': 2,
'circle-stroke-color': '#ff0000'
}
});
}
function addPoint(coords) {
if(jsonPoint.features.length > 0) {
var prev = jsonPoint.features[jsonPoint.features.length - 1];
jsonLine.features.push({
type: 'Feature',
geometry: {
type: 'LineString',
coordinates: [prev.geometry.coordinates, coords]
}
});
map.getSource('line').setData(jsonLine);
}
jsonPoint.features.push({
type: 'Feature',
geometry: {
type: 'Point',
coordinates: coords
}
});
map.getSource('points').setData(jsonPoint);
}
map.on('click', function (_e) {
if(isMeasure) {
var coords = [_e.lngLat.lng, _e.lngLat.lat];
addPoint(coords);
//points.push(coords);
}
});
map.on('mousemove', function (_e) {
if(isMeasure) {
var coords = [_e.lngLat.lng, _e.lngLat.lat];
if (jsonPoint.features.length > 0) {
var prev = jsonPoint.features[jsonPoint.features.length - 1];
var json = {
type: 'Feature',
geometry: {
type: 'LineString',
coordinates: [prev.geometry.coordinates, coords]
}
};
map.getSource('line-move').setData(json);
}
}
});
map.on('dblclick', function (_e) {
if(isMeasure) {
var coords = [_e.lngLat.lng, _e.lngLat.lat];
addPoint(coords);
isMeasure = false;
map.getCanvas().style.cursor = 'grab'
jsonPoint.features = [];
jsonLine.features = [];
}
});
});
创建polygon(多边形)绘制工具
$('#polygon').click(function (e) {
map.getCanvas().style.cursor = 'crosshair';
e.stopPropagation();
clearLayerAndSource();
var isMeasure = true;
// 禁止双击缩放
map.doubleClickZoom.disable();
var jsonPoint = {
'type': 'FeatureCollection',
'features': []
};
var jsonLine = {
'type': 'FeatureCollection',
'features': []
};
var points = [];
var source = map.getSource('points-area');
if(source) {
map.getSource('points-area').setData(jsonPoint);
map.getSource('line-area').setData(jsonLine);
} else {
map.addSource('points-area', {
type: 'geojson',
data: jsonPoint
});
map.addSource('line-area', {
type: 'geojson',
data: jsonLine
});
map.addLayer({
id: 'line-area',
type: 'fill',
source: 'line-area',
paint: {
'fill-color': '#ff0000',
'fill-opacity': 0.1
}
});
map.addLayer({
id: 'line-area-stroke',
type: 'line',
source: 'line-area',
paint: {
'line-color': '#ff0000',
'line-width': 2,
'line-opacity': 0.65
}
});
map.addLayer({
id: 'points-area',
type: 'circle',
source: 'points-area',
paint: {
'circle-color': '#ffffff',
'circle-radius': 3,
'circle-stroke-width': 2,
'circle-stroke-color': '#ff0000'
}
});
}
function addPoint(coords) {
jsonPoint.features.push({
type: 'Feature',
geometry: {
type: 'Point',
coordinates: coords
}
});
map.getSource('points-area').setData(jsonPoint);
}
map.on('click', function (_e) {
if(isMeasure) {
var coords = [_e.lngLat.lng, _e.lngLat.lat];
points.push(coords);
addPoint(coords);
}
});
map.on('dblclick', function (_e) {
if(isMeasure) {
var coords = [_e.lngLat.lng, _e.lngLat.lat];
points.push(coords);
isMeasure = false;
map.getCanvas().style.cursor = 'grab'
}
});
map.on('mousemove', function (_e) {
if(isMeasure) {
var coords = [_e.lngLat.lng, _e.lngLat.lat];
var len = jsonPoint.features.length;
if(len != 0 && len !=1){
var pts = points.concat([coords]);
pts = pts.concat([points[0]]);
var json = {
type: 'Feature',
geometry: {
type: 'Polygon',
coordinates: [pts]
}
};
map.getSource('line-area').setData(json);
}
}
});
})
创建 rectangle(矩形)绘制工具
$("#rectangle").click(function (e) {
debugger
map.getCanvas().style.cursor = 'crosshair';
e.stopPropagation();
clearLayerAndSource();
var isDraw = true;
// 禁止双击缩放
map.doubleClickZoom.disable();
var jsonPoint = {
'type': 'FeatureCollection',
'features': []
};
jsonPoint.features=[];
var source = map.getSource('rectangle');
if(source) {
map.getSource('rectangle').setData(jsonPoint);
}else{
map.addSource('rectangle', {
type: 'geojson',
data: jsonPoint
});
map.addLayer({
"id": "rectangle",
"type": "fill",
"source": "rectangle",
"layout": {},
"paint": {
"fill-color": "#e6205e",
"fill-opacity": 0.6
}
});
}
var starCoords=[];
let isMousemove=false;
map.on('click', function (_e) {
if (isDraw) {
starCoords = [_e.lngLat.lng, _e.lngLat.lat];
isMousemove=true;
}
})
var moveCoords=[];
map.on('mousemove', function (_e) {
if (isDraw && isMousemove) {
moveCoords = [_e.lngLat.lng, _e.lngLat.lat];
var rightTopCoords=[];
rightTopCoords=[moveCoords[0],starCoords[1]];
var buttomLeftCoords=[];
buttomLeftCoords=[starCoords[0],moveCoords[1]];
var coords=[];
coords=[starCoords,rightTopCoords,moveCoords,buttomLeftCoords]
jsonPoint.features=[];
jsonPoint.features.push({
type: 'Feature',
geometry: {
type: 'Polygon',
coordinates: [coords]
}
});
map.getSource('rectangle').setData(jsonPoint);
}
})
map.on('dblclick', function (_e) {
debugger
if (isDraw) {
isMousemove=false;
isDraw=false;
map.getCanvas().style.cursor = 'grab'
var endCoords = [_e.lngLat.lng, _e.lngLat.lat];
var rightTopCoords=[];
rightTopCoords=[endCoords[0],starCoords[1]];
var buttomLeftCoords=[];
buttomLeftCoords=[starCoords[0],endCoords[1]];
var coords=[];
coords=[starCoords,rightTopCoords,endCoords,buttomLeftCoords]
jsonPoint.features.push({
type: 'Feature',
geometry: {
type: 'Polygon',
coordinates: coords
}
});
map.getSource('rectangle').setData(jsonPoint);
}
})
})
清除图层(layer)和资源(source)
//清除draw资源图层
function clearLayerAndSource(){
if (map.getLayer('circle')) {
map.removeLayer('circle')
}
if (map.getSource('circle')) {
map.removeSource('circle');
}
if (map.getLayer('points')) {
map.removeLayer('points')
}
if (map.getSource('points')) {
map.removeSource('points');
}
if (map.getLayer('line')) {
map.removeLayer('line')
}
if (map.getSource('line')) {
map.removeSource('line');
}
if (map.getLayer('polygon')) {
map.removeLayer('polygon')
}
if (map.getSource('polygon')) {
map.removeSource('polygon');
}
if (map.getLayer('rectangle')) {
map.removeLayer('rectangle')
}
if (map.getSource('rectangle')) {
map.removeSource('rectangle');
}
if (map.getLayer('line-move')) {
map.removeLayer('line-move')
}
if (map.getSource('line-move')) {
map.removeSource('line-move');
}
if (map.getLayer('points-area')) {
map.removeLayer('points-area')
}
if (map.getSource('points-area')) {
map.removeSource('points-area');
}
if (map.getLayer('line-area-stroke')) {
map.removeLayer('line-area-stroke')
}
if (map.getSource('line-area-stroke')) {
map.removeSource('line-area-stroke');
}
if (map.getLayer('line-area')) {
map.removeLayer('line-area')
}
if (map.getSource('line-area')) {
map.removeSource('line-area');
}
}
以上对于有前端基础的小伙伴来说,代码实现逻辑不复杂,如果有疑问,可以加qq(1835334431)
除了以上基础绘制之外,还有如矢量图层(line,polygon,point)在地图上拖拽,编辑,计算面积,测量,数据渲染,数据分析,可以去案例中查看。
1:对了,之前也有小伙伴qq找我,在文章中发现的一些bug,我本地已经修护,只是最近工作上比较忙,文章没有更新,如果需要参考的小伙伴qq联系我,工作期间处于在线状态,随时可以联系,
2:还有一些小伙伴可能在开发中运用了前端框架,什么vue ,svelte等框架,需要集成进去,这不难,我们知道这些框架底层都是js库,转个弯拿过去就可以直接用,实在不知道在框架中怎么使用的,可以联系我,my这边也帮助一些小伙伴也写了一些demo
3: 一下地址是vue2中集成了对mapbox及cesium实现的一些小案例。有兴趣可以参考,有问题欢迎指出。
案例地址:
4,最重要一点,如果文章对你有帮助,给个赞