You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在React Native Mapbox GL中实现多边形绘制?

React Native Mapbox GL 多边形绘制实现方案

RN端@rnmapbox/maps没有内置Web端mapbox-gl-draw那样的开箱即用绘制控件,直接基于地图交互事件+矢量图层手动实现即可,逻辑和Web端绘制流程完全对齐,下面是可直接跑通的实现方案。

核心实现逻辑

  • 维护绘制状态标记、多边形顶点坐标集合两个核心状态
  • 监听MapView的点击事件,处于绘制模式时将点击位置的经纬度推入顶点数组
  • 基于ShapeSource将顶点数据转为GeoJSON,叠加填充层、线层、圆点层分别渲染多边形面、绘制连线、顶点标记
  • 提供开始绘制、撤销上点、清空、完成绘制的操作入口,完成绘制时闭合多边形(将首顶点追加到坐标数组末尾)即可拿到最终多边形数据

可直接复用的代码样例

import React, { useState, useCallback } from 'react';
import { View, Button, StyleSheet } from 'react-native';
import MapboxGL, {
  MapView,
  ShapeSource,
  FillLayer,
  LineLayer,
  CircleLayer,
  Camera,
} from '@rnmapbox/maps';

// 提前在项目初始化位置完成Mapbox AccessToken配置即可
MapboxGL.setAccessToken('你的项目Mapbox AccessToken');

const PolygonDrawMap = () => {
  const [isDrawing, setIsDrawing] = useState(false);
  const [coordinates, setCoordinates] = useState([]);

  // 地图点击收集顶点
  const handleMapPress = useCallback((e) => {
    if (!isDrawing) return;
    const newPoint = e.geometry.coordinates;
    setCoordinates(prev => [...prev, newPoint]);
  }, [isDrawing]);

  const handleUndo = useCallback(() => {
    setCoordinates(prev => prev.slice(0, -1));
  }, []);

  const handleClear = useCallback(() => {
    setCoordinates([]);
    setIsDrawing(false);
  }, []);

  const handleFinishDraw = useCallback(() => {
    if (coordinates.length < 3) {
      console.warn('多边形至少需要选择3个顶点');
      return;
    }
    // 闭合多边形
    const closedPolygon = [...coordinates, coordinates[0]];
    console.log('最终多边形坐标:', closedPolygon);
    setIsDrawing(false);
    // 此处写入你的业务逻辑,比如存状态、提交接口、计算面积等
  }, [coordinates]);

  // 多边形面GeoJSON
  const polygonGeoJSON = {
    type: 'FeatureCollection',
    features: [
      {
        type: 'Feature',
        geometry: {
          type: 'Polygon',
          coordinates: [coordinates.length >= 3 ? [...coordinates, coordinates[0]] : []],
        },
      },
    ],
  };

  // 顶点标记GeoJSON
  const pointsGeoJSON = {
    type: 'FeatureCollection',
    features: coordinates.map((coord, idx) => ({
      type: 'Feature',
      id: idx,
      geometry: {
        type: 'Point',
        coordinates: coord,
      },
    })),
  };

  return (
    <View style={styles.page}>
      <View style={styles.toolBar}>
        {!isDrawing ? (
          <Button title="开始绘制多边形" onPress={() => setIsDrawing(true)} />
        ) : (
          <>
            <Button title="撤销上点" onPress={handleUndo} disabled={coordinates.length === 0}/>
            <Button title="清空绘制" onPress={handleClear} />
            <Button title="完成绘制" onPress={handleFinishDraw} />
          </>
        )}
      </View>
      <MapView style={styles.map} onPress={handleMapPress}>
        <Camera
          defaultSettings={{
            centerCoordinate: [116.397428, 39.90923], // 替换为你的项目默认中心点
            zoomLevel: 12,
          }}
        />
        {/* 渲染多边形填充面 */}
        {coordinates.length >= 3 && (
          <ShapeSource id="polygon-source" shape={polygonGeoJSON}>
            <FillLayer
              id="polygon-fill"
              style={{
                fillColor: 'rgba(66, 133, 244, 0.3)',
                fillOutlineColor: 'rgba(66, 133, 244, 1)',
              }}
            />
          </ShapeSource>
        )}
        {/* 渲染绘制过程中的顶点连线 */}
        {coordinates.length >= 2 && (
          <ShapeSource
            id="line-source"
            shape={{
              type: 'Feature',
              geometry: {
                type: 'LineString',
                coordinates: coordinates,
              },
            }}
          >
            <LineLayer
              id="draw-line"
              style={{
                lineColor: '#4285F4',
                lineWidth: 2,
                lineDasharray: [2, 2],
              }}
            />
          </ShapeSource>
        )}
        {/* 渲染已选顶点标记 */}
        {coordinates.length > 0 && (
          <ShapeSource id="points-source" shape={pointsGeoJSON}>
            <CircleLayer
              id="point-mark"
              style={{
                circleRadius: 6,
                circleColor: '#4285F4',
                circleStrokeWidth: 2,
                circleStrokeColor: '#ffffff',
              }}
            />
          </ShapeSource>
        )}
      </MapView>
    </View>
  );
};

const styles = StyleSheet.create({
  page: { flex: 1 },
  map: { flex: 1 },
  toolBar: {
    position: 'absolute',
    top: 40,
    left: 16,
    right: 16,
    zIndex: 99,
    flexDirection: 'row',
    gap: 8,
    backgroundColor: '#ffffff',
    padding: 8,
    borderRadius: 8,
  },
});

export default PolygonDrawMap;

可扩展的优化方向

  • 支持顶点拖拽编辑:给顶点的CircleLayer绑定点击事件标记选中状态,监听地图拖拽事件更新对应顶点的坐标,即可实现和Web端一致的拖拽改形能力
  • 支持多多边形绘制:将coordinates状态改为二维数组,每个子项存储一个独立多边形的顶点集合,批量渲染所有多边形图层即可
  • 面积/周长计算:引入@turf/turf库,传入闭合后的多边形GeoJSON即可直接算出对应几何数值
  • 实时引导线:监听地图的手势移动事件,实时渲染当前触摸点和上一个顶点的虚线段,交互体验和官方Web控件完全对齐

内容的提问来源于stack exchange,提问作者Swift

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.02 21:10:09