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

React Native Mapbox GL如何使用turf.js计算绘制多边形面积

React Native Mapbox GL 基于Turf.js实现多边形面积计算

实现步骤

1. 安装依赖

在项目根目录执行命令安装Turf.js:
npm install @turf/turf
如果你使用yarn作为包管理工具,执行:
yarn add @turf/turf

2. 核心计算逻辑

Turf.js内置的area方法原生兼容Mapbox使用的WGS84经纬度坐标系,直接传入符合GeoJSON规范的Polygon/Feature对象即可返回面积值,返回结果默认单位为平方米,可根据业务需要自行转换为平方公里、公顷、亩等单位。
计算逻辑不需要额外做坐标偏移、投影转换,和Web端Mapbox绘制工具的面积计算结果完全一致。

3. 代码改造

首先在文件头部引入依赖和需要的React hooks:

import React, { useState, useEffect } from "react";
import * as turf from '@turf/turf';

新增状态存储计算得到的面积值:
const [polygonArea, setPolygonArea] = useState(0);
添加监听逻辑,当多边形坐标更新时自动重新计算面积:

useEffect(() => {
  // 校验多边形数据合法性,至少3个点才能构成有效多边形
  if (polygon?.geometry?.coordinates?.[0]?.length >= 3) {
    const calculatedArea = turf.area(polygon);
    setPolygonArea(calculatedArea);
  }
}, [polygon]);

添加面积展示组件,放在地图上层即可:

<View style={styles.areaDisplay}>
  <Text style={styles.areaText}>
    绘制面积:
    {polygonArea < 1000000 
      ? `${polygonArea.toFixed(2)} ㎡` 
      : `${(polygonArea / 1000000).toFixed(2)} k㎡`}
  </Text>
</View>

在样式表中补充对应样式:

areaDisplay: {
  position: "absolute",
  top: 24,
  left: 16,
  backgroundColor: "#fff",
  paddingHorizontal: 14,
  paddingVertical: 10,
  borderRadius: 8,
  zIndex: 99,
  elevation: 4
},
areaText: {
  fontSize: 15,
  color: "#222"
}

注意事项

  • 传入计算的多边形必须是闭合结构,即坐标数组的第一个点和最后一个点坐标完全一致,否则会出现计算误差。现有代码中的示例多边形已经满足闭合要求,后续开发手绘交互时,绘制结束时需要手动补全闭合点。
  • 该方法同时支持MultiPolygon类型、带内环孔洞的多边形面积计算,不需要额外做兼容处理。
  • 如果需要做面积实时计算,只需要在手绘过程中每次更新polygon状态即可,useEffect会自动触发重新计算。

完整改造后的代码

import React, { useState, useEffect } from "react";
import { View, StyleSheet, Text } from "react-native";
import MapboxGL from "@react-native-mapbox-gl/maps";
import * as turf from '@turf/turf';

MapboxGL.setAccessToken(
  "pk.eyJ1IjoiZGVla3NoYW1laHRhMTI1IiwiYSI6ImNrcWV6OWE0bDBjcmMydXF1enZqMjd5MDMifQ.hFB7SI_kojKYfNQ42c62BA"
);

const App = () => {
  const [coordinates] = useState([78.9629, 20.5937]);
  const [polygonArea, setPolygonArea] = useState(0);
  const [route, setRoute] = useState({
    type: "FeatureCollection",
    features: [
      {
        type: "Feature",
        properties: {},
        geometry: {
          type: "LineString",
          coordinates: [
            [77.5946, 12.9716],
            [80.2707, 13.0827],
          ],
        },
      },
    ],
  });

  const [polygon, setPolygon] = useState({
    type: "Feature",
    geometry: {
      type: "Polygon",
      coordinates: [
        [
          [72.685547, 20.055931],
          [76.640625, 21.207458],
          [76.904297, 17.978733],
          [72.685547, 20.055931],
        ],
      ],
    },
  });

  useEffect(() => {
    if (polygon?.geometry?.coordinates?.[0]?.length >= 3) {
      const calculatedArea = turf.area(polygon);
      setPolygonArea(calculatedArea);
    }
  }, [polygon]);

  const renderAnnotations = () => {
    return (
      <MapboxGL.PointAnnotation
        key="pointAnnotation"
        id="pointAnnotation"
        coordinate={[74, 27]}
      >
        <View
          style={{
            height: 30,
            width: 30,
            backgroundColor: "red",
            borderRadius: 50,
            borderColor: "#fff",
            borderWidth: 3,
          }}
        />
      </MapboxGL.PointAnnotation>
    );
  };

  return (
    <View style={styles.page}>
      <View style={styles.container}>
        <MapboxGL.MapView style={styles.map} onPress={() => console.log("test")}>
          <MapboxGL.Camera zoomLevel={4} centerCoordinate={coordinates} />
          <MapboxGL.PointAnnotation coordinate={coordinates} />
          <View>{renderAnnotations()}</View>
          <MapboxGL.MarkerView id={"marker"} coordinate={[72, 23]}>
            <View>
              <View style={styles.markerContainer}>
                <View style={styles.textContainer}>
                  <Text style={styles.text}>{"Gujarat"}</Text>
                </View>
              </View>
            </View>
          </MapboxGL.MarkerView>
          <MapboxGL.ShapeSource id="line1" shape={route}>
            <MapboxGL.LineLayer
              id="linelayer1"
              style={{ lineColor: "red", lineWidth: 5 }}
            />
          </MapboxGL.ShapeSource>
          <MapboxGL.ShapeSource id="source" shape={polygon}>
            <MapboxGL.FillLayer
              id="fill"
              style={{ fillColor: "blue", fillOpacity: 0.7 }}
            />
            <MapboxGL.LineLayer
              id="line"
              style={{ lineColor: "red", lineWidth: 2 }}
            />
          </MapboxGL.ShapeSource>
        </MapboxGL.MapView>
        {/* 面积展示层 */}
        <View style={styles.areaDisplay}>
          <Text style={styles.areaText}>
            绘制面积:
            {polygonArea < 1000000 
              ? `${polygonArea.toFixed(2)} ㎡` 
              : `${(polygonArea / 1000000).toFixed(2)} k㎡`}
          </Text>
        </View>
      </View>
    </View>
  );
};

const styles = StyleSheet.create({
  page: {
    flex: 1,
  },
  container: {
    height: "100%",
    width: "100%",
    flex: 1,
  },
  markerContainer: {
    alignItems: "center",
    width: 60,
    backgroundColor: "transparent",
    height: 70,
  },
  map: {
    flex: 1,
  },
  textContainer: {
    backgroundColor: "white",
    borderRadius: 10,
    flex: 1,
    flexDirection: "row",
    alignItems: "center",
  },
  text: {
    textAlign: "center",
    paddingHorizontal: 5,
    flex: 1,
  },
  icon: {
    paddingTop: 10,
  },
  areaDisplay: {
    position: "absolute",
    top: 24,
    left: 16,
    backgroundColor: "#fff",
    paddingHorizontal: 14,
    paddingVertical: 10,
    borderRadius: 8,
    zIndex: 99,
    elevation: 4
  },
  areaText: {
    fontSize: 15,
    color: "#222"
  }
});

export default App;

内容的提问来源于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:21:36