使用Mapbox GL Draw绘制多边形
使用Mapbox GL Draw插件绘制多边形;
<!DOCTYPE html>
<html lang="en">
<head>
<title>使用Mapbox GL Draw绘制多边形</title>
<meta property="og:description" content="使用Mapbox GL Draw插件绘制多边形" />
<meta charset='utf-8'>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel='stylesheet' href='https://unpkg.com/maplibre-gl@5.5.0/dist/maplibre-gl.css' />
<script src='https://unpkg.com/maplibre-gl@5.5.0/dist/maplibre-gl.js'></script>
<link rel='stylesheet' href='https://api.mapbox.com/mapbox-gl-js/plugins/mapbox-gl-draw/v1.4.0/mapbox-gl-draw.css' type='text/css' />
<script src='https://api.mapbox.com/mapbox-gl-js/plugins/mapbox-gl-draw/v1.4.0/mapbox-gl-draw.js'></script>
<style>
body { margin: 0; padding: 0; }
html, body, #map { height: 100%; }
.calculation-box {
height: 140px;
width: 250px;
position: absolute;
bottom: 20px;
left: 10px;
background-color: rgba(255, 255, 255, 0.9);
padding: 15px;
text-align: center;
font-family: 'Arial', sans-serif;
border-radius: 5px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}
p {
font-weight: bold;
margin-bottom: 5px;
}
.measurement-value {
margin-top: 5px;
padding: 5px;
background-color: #f8f8f8;
border-radius: 3px;
font-size: 14px;
}
</style>
</head>
<body>
<div id="map"></div>
<div class="calculation-box">
<p>绘制一个多边形,使用绘图工具:</p>
<div id="calculated-area" class="measurement-value">
总面积:0 平方米
</div>
<div id="calculated-perimeter" class="measurement-value">
周长:0 米
</div>
<div id="vertex-count" class="measurement-value">
顶点数:0
</div>
</div>
<script>
// 初始化地图
const map = new maplibregl.Map({
container: 'map',
style: 'https://api.maptiler.com/maps/streets/style.json?key=get_your_own_OpIi9ZULNHzrESv6T2vL',
center: [0, 0],
zoom: 2
});
// 初始化绘图控件
const draw = new MapboxDraw({
displayControlsDefault: false, // 隐藏所有控件
controls: {
polygon: true, // 仅显示多边形工具
trash: true // 显示删除按钮
},
// 自定义绘图样式
styles: [
// 绘制点样式
{
'id': 'gl-draw-polygon-fill-inactive',
'type': 'fill',
'filter': ['all', ['==', 'active', 'false'], ['==', '$type', 'Polygon']],
'paint': {
'fill-color': '#3bb2d0',
'fill-outline-color': '#3bb2d0',
'fill-opacity': 0.3
}
},
// 活动填充样式
{
'id': 'gl-draw-polygon-fill-active',
'type': 'fill',
'filter': ['all', ['==', 'active', 'true'], ['==', '$type', 'Polygon']],
'paint': {
'fill-color': '#fbb03b',
'fill-outline-color': '#fbb03b',
'fill-opacity': 0.3
}
},
// 顶点中间线样式
{
'id': 'gl-draw-polygon-midpoint',
'type': 'circle',
'filter': ['all', ['==', '$type', 'Point'], ['==', 'meta', 'midpoint']],
'paint': {
'circle-radius': 3,
'circle-color': '#fbb03b'
}
},
// 多边形轮廓线样式
{
'id': 'gl-draw-polygon-stroke-inactive',
'type': 'line',
'filter': ['all', ['==', 'active', 'false'], ['==', '$type', 'Polygon']],
'layout': {
'line-cap': 'round',
'line-join': 'round'
},
'paint': {
'line-color': '#3bb2d0',
'line-width': 2
}
},
// 激活状态的线条样式
{
'id': 'gl-draw-polygon-stroke-active',
'type': 'line',
'filter': ['all', ['==', 'active', 'true'], ['==', '$type', 'Polygon']],
'layout': {
'line-cap': 'round',
'line-join': 'round'
},
'paint': {
'line-color': '#fbb03b',
'line-dasharray': [0.2, 2],
'line-width': 2
}
},
// 顶点样式
{
'id': 'gl-draw-polygon-and-line-vertex-inactive',
'type': 'circle',
'filter': ['all', ['==', 'meta', 'vertex'], ['==', '$type', 'Point']],
'paint': {
'circle-radius': 5,
'circle-color': '#fff',
'circle-stroke-color': '#3bb2d0',
'circle-stroke-width': 2
}
},
// 活动顶点样式
{
'id': 'gl-draw-polygon-and-line-vertex-active',
'type': 'circle',
'filter': ['all', ['==', 'meta', 'vertex'], ['==', '$type', 'Point'], ['==', 'active', 'true']],
'paint': {
'circle-radius': 6,
'circle-color': '#fbb03b',
'circle-stroke-color': '#fff',
'circle-stroke-width': 2
}
}
]
});
// 添加绘图控件到地图
map.addControl(draw);
// 添加导航控件
map.addControl(new maplibregl.NavigationControl());
// 使用Turf.js计算绘制的多边形面积和周长
map.on('draw.create', updateArea);
map.on('draw.delete', updateArea);
map.on('draw.update', updateArea);
// 计算面积函数
function updateArea() {
const data = draw.getAll();
const area_element = document.getElementById('calculated-area');
const perimeter_element = document.getElementById('calculated-perimeter');
const vertex_element = document.getElementById('vertex-count');
if (data.features.length > 0) {
// 获取最后一个绘制的多边形
const polygon = data.features[data.features.length - 1];
if (polygon.geometry.type === 'Polygon') {
// 计算面积(平方米)
const area = calculateArea(polygon);
// 格式化面积显示
let areaStr;
if (area >= 1000000) {
areaStr = `${(area / 1000000).toFixed(2)} 平方公里`;
} else if (area >= 10000) {
areaStr = `${(area / 10000).toFixed(2)} 公顷`;
} else {
areaStr = `${Math.round(area)} 平方米`;
}
area_element.textContent = `总面积:${areaStr}`;
// 计算周长(米)
const perimeter = calculatePerimeter(polygon);
// 格式化周长显示
let perimeterStr;
if (perimeter >= 1000) {
perimeterStr = `${(perimeter / 1000).toFixed(2)} 公里`;
} else {
perimeterStr = `${Math.round(perimeter)} 米`;
}
perimeter_element.textContent = `周长:${perimeterStr}`;
// 计算顶点数
const vertices = polygon.geometry.coordinates[0].length - 1; // 减去1因为首尾点相同
vertex_element.textContent = `顶点数:${vertices}`;
}
} else {
// 如果没有多边形,重置显示
area_element.textContent = '总面积:0 平方米';
perimeter_element.textContent = '周长:0 米';
vertex_element.textContent = '顶点数:0';
}
}
// 简单的球面面积计算函数(基于Haversine公式的近似值)
function calculateArea(polygon) {
const coordinates = polygon.geometry.coordinates[0];
// 使用简化版的球面多边形面积计算
let area = 0;
if (coordinates.length > 3) { // 至少需要3个点形成多边形(加上闭合点)
const R = 6371000; // 地球半径,单位:米
for (let i = 0; i < coordinates.length - 1; i++) {
const p1 = coordinates[i];
const p2 = coordinates[i + 1];
// 将经纬度转换为弧度
const lat1 = p1[1] * Math.PI / 180;
const lng1 = p1[0] * Math.PI / 180;
const lat2 = p2[1] * Math.PI / 180;
const lng2 = p2[0] * Math.PI / 180;
// 计算球面三角形面积
area += (lng2 - lng1) * (2 + Math.sin(lat1) + Math.sin(lat2));
}
area = Math.abs(area * R * R / 2);
}
return area;
}
// 计算多边形周长
function calculatePerimeter(polygon) {
const coordinates = polygon.geometry.coordinates[0];
let perimeter = 0;
if (coordinates.length > 1) {
for (let i = 0; i < coordinates.length - 1; i++) {
perimeter += distance(
coordinates[i][1], coordinates[i][0],
coordinates[i + 1][1], coordinates[i + 1][0]
);
}
}
return perimeter;
}
// 使用Haversine公式计算两点间距离
function distance(lat1, lon1, lat2, lon2) {
const R = 6371000; // 地球半径,单位:米
const dLat = (lat2 - lat1) * Math.PI / 180;
const dLon = (lon2 - lon1) * Math.PI / 180;
const a =
Math.sin(dLat/2) * Math.sin(dLat/2) +
Math.cos(lat1 * Math.PI / 180) * Math.cos(lat2 * Math.PI / 180) *
Math.sin(dLon/2) * Math.sin(dLon/2);
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
const d = R * c;
return d;
}
</script>
</body>
</html>