React Native携手ArcGIS:SketchEditorCtrl地图开发实战
# React Native携手ArcGIS:SketchEditorCtrl地图开发实战
在移动端开发中,地图交互已成为很多应用的核心功能之一。例如:地块标绘、路径规划、地理信息采集等。本文将通过“React Native + ArcGIS Runtime SDK for Android/iOS”结合 `SketchEditorCtrl`(Sketch Editor 控制器)示例,深入演示如何在 React Native 中集成 ArcGIS 地图,并实现在线标绘、编辑要素等实战功能。文章内容包含环境准备、安装配置、核心代码示例、图解与详细说明,帮助你快速上手并轻松掌握。
---
## 一、环境准备
1. **前提条件**
- 已安装 **Node.js(建议 v14+)**、**Yarn** 或 **npm**。
- 已安装并配置好 **Android Studio**(SDK 版本 29+)、**Xcode 12+(仅限 macOS)**。
- React Native 开发环境已经搭建完毕,可执行 `npx react-native run-android` 或 `npx react-native run-ios` 创建并运行空白项目。
2. **ArcGIS 开发者账号与 API Key**
- 访问 [ArcGIS Developer](https://developers.arcgis.com/) 注册开发者账号。
- 在 Dashboard 中申请一个 **API Key**,后续在代码中用于初始化 ArcGIS 服务。
3. **ArcGIS Runtime SDK**
- 本文使用 Esri 官方提供的 **ArcGIS Runtime SDK**,并结合社区维护的 React Native 组件 `react-native-arcgis-mapview`。
- 该组件底层封装了 Android 的 `com.esri.arcgisruntime.mapping.view.MapView` 与 iOS 的 `AGSMapView`,并对外暴露初始化、加载地图、SketchEditor、Graphic 等常用接口。
4. **创建 RN 项目**
```bash
npx react-native init RNArcGISSketchDemo
cd RNArcGISSketchDemo
二、安装与原生配置
2.1 安装 react-native-arcgis-mapview
# 使用 Yarn
yarn add react-native-arcgis-mapview
# 或 NPM
# npm install react-native-arcgis-mapview --save
此时,会在 node_modules/react-native-arcgis-mapview
目录下看到对应 Android 与 iOS 原生模块。
2.2 iOS 原生配置
进入 iOS 目录并安装 CocoaPods 依赖
cd ios pod install --repo-update cd ..
在
Info.plist
中添加 ArcGIS 权限及 API Key
打开ios/RNArcGISSketchDemo/Info.plist
,添加下面几行:<!-- ArcGIS 许可与各种权限 --> <key>AGSAppClientID</key> <string>YOUR_ARCGIS_API_KEY</string> <key>NSLocationWhenInUseUsageDescription</key> <string>应用需要获取您的位置用于地图标绘</string>
- 将
YOUR_ARCGIS_API_KEY
替换为你在 ArcGIS Developer Dashboard 中生成的 API Key。 NSLocationWhenInUseUsageDescription
是定位权限,用于在地图上显示用户当前位置及编辑周边要素。
- 将
确保 iOS Deployment Target
- 在 Xcode 中选择项目 Target → General → Deployment Info → 将 iOS Deployment Target 设置为 13.0 及以上。
2.3 Android 原生配置
修改
android/app/build.gradle
在android/app/build.gradle
中,向defaultConfig
中添加 ArcGIS API Key 配置:android { defaultConfig { applicationId "com.rnarcgissketchdemo" minSdkVersion 21 targetSdkVersion 30 versionCode 1 versionName "1.0" // 添加以下一行 manifestPlaceholders = [ "AGSCredentials:YOUR_ARCGIS_API_KEY" ] } ... }
- 注意将
YOUR_ARCGIS_API_KEY
替换为实际值。
- 注意将
修改
android/app/src/main/AndroidManifest.xml
在<application>
标签中添加权限及 metadata:<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.rnarcgissketchdemo"> <!-- 地理定位权限 --> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <application android:name=".MainApplication" android:label="@string/app_name" android:icon="@mipmap/ic_launcher" android:roundIcon="@mipmap/ic_launcher_round" android:allowBackup="false" android:theme="@style/AppTheme"> <!-- ArcGIS API Key --> <meta-data android:name="com.esri.arcgisruntime.ArcGISRuntime_LICENSE" android:value="${AGSCredentials}" /> <activity android:name=".MainActivity" android:exported="true" android:label="@string/app_name"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>
编译验证
npx react-native run-android
- 确认 Android 模拟器或真机正常编译并启动,若出现 ArcGIS 许可错误,请检查 API Key 配置是否正确。
三、SketchEditorCtrl 核心概念
在 ArcGIS 中,Sketch Editor(要素绘制器)允许用户在地图上“绘制”或“编辑”点、线、面等几何要素。React Native 端使用的 SketchEditorCtrl
即是对原生 SketchEditor
的封装,常见功能包括:
- 开始绘制:启动标绘模式,指定几何类型(点
Point
、折线Polyline
、多边形Polygon
)。 - 绘制过程:用户点击/拖动时,通过
SketchEditorCtrl
收集屏幕坐标并实时将结果渲染在地图上。 - 完成绘制:触发
complete
事件,获取最终几何要素,通常以 GeoJSON 或 ArcGIS 几何对象形式返回,后续可用于保存到本地或发送服务端。 - 编辑已绘制要素:可加载已有几何,切换到“编辑”模式,支持移动顶点、拉伸等操作。
- 取消/撤销:支持撤销上一步操作、取消整个绘制。
3.1 SketchEditorCtrl 核心 API
以下示例展示 react-native-arcgis-mapview
对 Sketch Editor 的常用接口,并结合 React Native 组件的使用方式。
import ArcgisMapView, {
ArcGISMapType,
SketchEditorCtrl,
GeometryType, // 'point' | 'polyline' | 'polygon'
Graphic, // 用于渲染绘制图形
} from 'react-native-arcgis-mapview';
SketchEditorCtrl.startDraw(geometryType, options)
geometryType
:字符串,表示绘制类型,常见'point'
|'polyline'
|'polygon'
。options
:可选参数对象,例如{ symbol: { color: '#FF0000', width: 3 } }
指定图形符号样式。
SketchEditorCtrl.stopDraw()
- 停止当前绘制,隐藏辅助线及临时符号。
SketchEditorCtrl.clear()
- 清除已绘制的所有图形。
SketchEditorCtrl.onDrawComplete(callback)
- 绘制完成回调,函数参数为
geometry
对象,包含type
、coordinates
等信息。
- 绘制完成回调,函数参数为
SketchEditorCtrl.loadGeometry(geometry)
- 加载并进入“编辑模式”,接收一个已存在的几何对象,用户可以修改顶点位置。
SketchEditorCtrl.onVertexChanged(callback)
- 顶点移动时触发的回调,可用于 UI 协助(如实时显示当前坐标)。
四、核心代码示例
下面以一个“绘制并编辑多边形”功能为例,演示 React Native 端如何使用 SketchEditorCtrl
。最终实现效果是:点击“开始绘制”按钮,进入绘制模式;用户在地图上点击或拖动绘制多边形;绘制完成后,显示 GeoJSON 信息;再点击“编辑多边形”按钮,可加载已绘制多边形并进行顶点移动;最后点击“清除”按钮,清空所有绘制。
4.1 完整示例代码(App.js)
// App.js
import React, { useRef, useState, useEffect } from 'react';
import {
View,
Text,
Button,
StyleSheet,
Dimensions,
SafeAreaView,
ScrollView,
} from 'react-native';
import ArcgisMapView, {
ArcGISMapType,
SketchEditorCtrl,
GeometryType,
Graphic,
GraphicLayerCtrl,
} from 'react-native-arcgis-mapview';
const { width, height } = Dimensions.get('window');
export default function App() {
// 1. 保存当前绘制的几何(GeoJSON 形式)
const [drawnGeometry, setDrawnGeometry] = useState(null);
// 2. 保存控制器引用
const sketchCtrl = useRef(null);
const graphicLayerCtrl = useRef(null);
// 3. 地图加载完成回调
const onMapLoad = () => {
console.log('地图加载完成');
};
// 4. 绘制完成回调
const onDrawComplete = (geometry) => {
console.log('绘制完成:', geometry);
// geometry 示例:{ type: 'polygon', rings: [ [lng,lat], [lng,lat], ... ] }
setDrawnGeometry(geometry);
// 将几何转换为 Graphic 并添加到图层
const graphic = new Graphic({
geometry: geometry,
symbol: {
type: 'simple-fill',
color: [255, 0, 0, 0.3],
outline: {
color: [255, 0, 0, 1],
width: 2,
},
},
});
graphicLayerCtrl.current.addGraphic(graphic);
};
// 5. 开始绘制多边形
const startDrawing = () => {
// 清空图层
graphicLayerCtrl.current.removeAllGraphics();
setDrawnGeometry(null);
sketchCtrl.current.startDraw(GeometryType.POLYGON, {
// 可选:设置临时绘制时的符号样式
symbol: {
type: 'simple-fill',
color: [0, 0, 255, 0.2],
outline: {
color: [0, 0, 255, 0.8],
width: 2,
},
},
// 可选:是否允许连续点击,默认 true
allowdrawing: true,
});
};
// 6. 停止绘制
const stopDrawing = () => {
sketchCtrl.current.stopDraw();
};
// 7. 编辑已绘制多边形
const startEditing = () => {
if (!drawnGeometry) {
alert('请先绘制多边形');
return;
}
// 加载几何进入编辑模式
sketchCtrl.current.loadGeometry(drawnGeometry, {
symbol: {
type: 'simple-fill',
color: [0, 255, 0, 0.2],
outline: { color: [0, 255, 0, 1], width: 2 },
},
});
// 监听顶点改变
sketchCtrl.current.onVertexChanged((updatedGeometry) => {
console.log('顶点改变:', updatedGeometry);
setDrawnGeometry(updatedGeometry);
// 更新图层:先清空再添加新的几何
graphicLayerCtrl.current.removeAllGraphics();
const g = new Graphic({
geometry: updatedGeometry,
symbol: {
type: 'simple-fill',
color: [0, 255, 0, 0.3],
outline: { color: [0, 255, 0, 1], width: 2 },
},
});
graphicLayerCtrl.current.addGraphic(g);
});
};
// 8. 清除
const clearAll = () => {
sketchCtrl.current.stopDraw();
graphicLayerCtrl.current.removeAllGraphics();
setDrawnGeometry(null);
};
return (
<SafeAreaView style={styles.safe}>
<View style={styles.container}>
{/* 1. ArcGIS 地图组件 */}
<ArcgisMapView
style={styles.map}
mapType={ArcGISMapType.STREETS_VECTOR}
onMapLoad={onMapLoad}
>
{/* 2. Graphic 图层:用来存放绘制完成的图形 */}
<GraphicLayerCtrl ref={graphicLayerCtrl} />
{/* 3. Sketch Editor 控制器 */}
<SketchEditorCtrl
ref={sketchCtrl}
onDrawComplete={onDrawComplete}
/>
</ArcgisMapView>
{/* 4. 底部操作按钮 */}
<View style={styles.toolbar}>
<Button title="开始绘制" onPress={startDrawing} />
<Button title="停止绘制" onPress={stopDrawing} />
<Button title="编辑多边形" onPress={startEditing} />
<Button title="清除所有" onPress={clearAll} />
</View>
{/* 5. 底部信息展示:当前绘制几何(JSON) */}
<View style={styles.info}>
<Text style={styles.infoTitle}>当前几何(GeoJSON):</Text>
<ScrollView style={styles.infoScroll}>
<Text style={styles.infoText}>
{drawnGeometry
? JSON.stringify(drawnGeometry, null, 2)
: '无内容'}
</Text>
</ScrollView>
</View>
</View>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
safe: { flex: 1, backgroundColor: '#fff' },
container: { flex: 1 },
map: {
width: width,
height: height * 0.6,
},
toolbar: {
flexDirection: 'row',
justifyContent: 'space-around',
paddingVertical: 8,
backgroundColor: '#f0f0f0',
},
info: {
flex: 1,
padding: 8,
backgroundColor: '#ffffff',
},
infoTitle: {
fontSize: 16,
fontWeight: 'bold',
},
infoScroll: {
marginTop: 4,
backgroundColor: '#fafafa',
borderColor: '#ddd',
borderWidth: 1,
borderRadius: 4,
padding: 4,
},
infoText: {
fontSize: 12,
color: '#333',
},
});
4.2 关键点详解
ArcgisMapView
组件mapType={ArcGISMapType.STREETS_VECTOR}
:加载 ESRI 官方“街道矢量”底图。onMapLoad={onMapLoad}
:地图加载完成后才可调用绘制或图层操作。
GraphicLayerCtrl
图层控制器- 用于承载所有绘制完成或编辑后的几何
Graphic
对象。 - 通过
ref
调用addGraphic()
、removeAllGraphics()
等方法管理图形集合。
- 用于承载所有绘制完成或编辑后的几何
SketchEditorCtrl
控制器- 通过
ref
访问其方法:startDraw()
、stopDraw()
、loadGeometry()
、onDrawComplete()
、onVertexChanged()
。 onDrawComplete(geometry)
回调触发时,表示用户已经完成一次“点选-双击”或“完成按钮”操作,geometry
以 ArcGIS 几何对象格式返回,本文直接将其保存到drawnGeometry
,并转换为Graphic
添加到图层。
- 通过
绘制与编辑流程
开始绘制:调用
sketchCtrl.current.startDraw(GeometryType.POLYGON, options)
进入多边形绘制模式。- 用户在地图上单击进行顶点添加,双击或长按结束绘制。
- 临时几何会以半透明方式渲染(由
options.symbol
控制)。
- 绘制完成:
onDrawComplete
拿到最终geometry
,再以新的Graphic
对象渲染在图层上(使用红色或其他样式)。 开始编辑:先判断
drawnGeometry
不为空,然后调用sketchCtrl.current.loadGeometry(drawnGeometry, options)
,进入几何编辑模式。- 用户可以拖动顶点修改形状,此时
onVertexChanged
不断回调,返回更新后的几何,可以用来实时更新GraphicLayer
或显示面积/周长等。
- 用户可以拖动顶点修改形状,此时
- 停止绘制/退出编辑:调用
sketchCtrl.current.stopDraw()
退出当前绘制或编辑状态,但已添加到图层上的Graphic
不受影响。 - 清除所有:调用
sketchCtrl.current.stopDraw()
停止绘制,然后graphicLayerCtrl.current.removeAllGraphics()
清空所有绘制结果,重置drawnGeometry
。
GeoJSON 信息展示
- 在底部
ScrollView
中,通过JSON.stringify
将drawnGeometry
(ArcGIS 原生几何格式)序列化,用于调试或复制到后端。 示例几何格式可能如下:
{ "type": "polygon", "rings": [ [116.391,39.907], [116.392,39.908], [116.390,39.908], [116.391,39.907] ] }
- 开发者可根据需要转换为标准 GeoJSON 或 ArcGIS JSON。
- 在底部
五、图解:工作流程与组件交互
为了更直观地理解上面代码的执行逻辑,下面以简化顺序图形式展示各组件和控制器之间的交互流程。
┌────────────────────────────────────────────────────────┐
│ 用户点击“开始绘制”按钮 │
└────────────────────────────────────────────────────────┘
│
▼
┌────────────────────────────────────────────────────────┐
│ App.js 中 startDrawing() 被调用 │
│ → sketchCtrl.current.startDraw('polygon', options) │
└────────────────────────────────────────────────────────┘
│
▼
┌────────────────────────────────────────────────────────┐
│ SketchEditorCtrl 原生模块 启动“绘制模式” │
│ • 在地图上拦截点击事件,添加临时顶点 │
│ • 将临时几何实时渲染到 MapView (带半透明符号) │
└────────────────────────────────────────────────────────┘
│
▼
┌────────────────────────────────────────────────────────┐
│ 用户在地图上连续点击/拖动完成多边形绘制 │
│ • 双击结束或点击“完成” │
└────────────────────────────────────────────────────────┘
│
▼
┌────────────────────────────────────────────────────────┐
│ SketchEditorCtrl 触发 onDrawComplete(geometry) 回调│
│ • geometry 即最终几何 │
└────────────────────────────────────────────────────────┘
│
▼
┌────────────────────────────────────────────────────────┐
│ App.js 中 onDrawComplete() 处理: │
│ • setDrawnGeometry(geometry) │
│ • 创建 new Graphic({ geometry, symbol }) │
│ • graphicLayerCtrl.current.addGraphic(graphic) │
└────────────────────────────────────────────────────────┘
│
▼
┌────────────────────────────────────────────────────────┐
│ 地图上新增一个“已完成多边形”图形(红色半透明) │
└────────────────────────────────────────────────────────┘
-------------------------------------------------------------
┌────────────────────────────────────────────────────────┐
│ 用户点击“编辑多边形”按钮 │
└────────────────────────────────────────────────────────┘
│
▼
┌────────────────────────────────────────────────────────┐
│ App.js 中 startEditing() 被调用 │
│ → sketchCtrl.current.loadGeometry(drawnGeometry, options) │
└────────────────────────────────────────────────────────┘
│
▼
┌────────────────────────────────────────────────────────┐
│ SketchEditorCtrl 加载已有几何进入“编辑模式” │
│ • 将已绘制多边形以绿色半透明符号渲染 │
│ • 在顶点处显示可拖动的控制点 │
└────────────────────────────────────────────────────────┘
│
▼
┌────────────────────────────────────────────────────────┐
│ 用户拖动顶点修改形状 │
│ • 每次顶点移动触发 onVertexChanged(updatedGeometry) │
└────────────────────────────────────────────────────────┘
│
▼
┌────────────────────────────────────────────────────────┐
│ App.js 中 onVertexChanged() 处理: │
│ • setDrawnGeometry(updatedGeometry) │
│ • graphicLayerCtrl.current.removeAllGraphics() │
│ • graphicLayerCtrl.current.addGraphic(新 geometry Graphic) │
└────────────────────────────────────────────────────────┘
│
▼
┌────────────────────────────────────────────────────────┐
│ 地图上“多边形”实时更新为用户拖动后的新形状(绿色半透明) │
└────────────────────────────────────────────────────────┘
-------------------------------------------------------------
┌────────────────────────────────────────────────────────┐
│ 用户点击“清除所有”按钮 │
└────────────────────────────────────────────────────────┘
│
▼
┌────────────────────────────────────────────────────────┐
│ App.js 中 clearAll() 被调用 │
│ • sketchCtrl.current.stopDraw() │
│ • graphicLayerCtrl.current.removeAllGraphics() │
│ • setDrawnGeometry(null) │
└────────────────────────────────────────────────────────┘
│
▼
┌────────────────────────────────────────────────────────┐
│ 地图上所有绘制内容与临时图形全部清除 │
└────────────────────────────────────────────────────────┘
- 事件触发:按钮点击 → 调用对应方法(开始绘制/编辑/清除)。
- SketchEditorCtrl:原生模块负责拦截地图触摸事件、生成几何并通知 JS。
- GraphicLayerCtrl:在 JS 层用
Graphic
对象渲染最终几何到地图图层。 - State 更新:通过
useState
保存几何,并在 UI 底部展示 GeoJSON 信息。
六、常见疑难与优化建议
地图偶尔不响应触摸绘制
- 确保
SketchEditorCtrl
已经正确初始化并在地图加载完成后才调用startDraw()
。 - 检查是否在
onMapLoad
回调后再执行startDraw
,否则地图还未完全加载时拦截事件会无效。
- 确保
多边形边界不封闭
SketchEditorCtrl
默认会自动将最后一个顶点与第一个顶点相连,确保闭合。如果发现不闭合,检查GeometryType
是否正确设置为'polygon'
。
编辑模式闪烁或多次添加图形
- 在
onVertexChanged
回调中,需先清空图层再添加新的Graphic
,否则会出现残留旧图形叠加。 - 可根据需求在
onVertexChanged
中加防抖(Debounce)处理,避免连续快速回调导致性能问题。
- 在
大数据量绘制性能
- 如果一次性绘制大量点/线/面,建议将要素分批添加,并在视图级别控制可见图层。
- ArcGIS Runtime SDK 会对大量矢量要素进行批量渲染优化,但在移动端仍要注意合理简化几何。
与 GPS 定位结合
- 可通过 React Native 的
react-native-geolocation-service
或Location
API 获取定位,再将坐标转换为 ArcGIS 坐标系(WGS84 → WebMercator),手动调用SketchEditorCtrl.loadGeometry
或动态添加Graphic
实时定位。 - ArcGIS GeoView 自带
LocationDisplay
模块,不过在 React Native 端需要使用原生模块或自行桥接。
- 可通过 React Native 的
离线地图与本地切片
如果需要在无网络环境下使用地图,可下载 ArcGIS 离线切片包 (Tile Package),并在
ArcgisMapView
中指定离线切片路径:<ArcgisMapView style={styles.map} mapType={ArcGISMapType.LOCAL_TILE} localTilePath={'/sdcard/arcgis/tiles.tpk'} onMapLoad={onMapLoad} />
- 然后在 SketchEditorCtrl 中照常绘制,所有几何操作都与离线地图兼容。
七、总结
本文通过“React Native 携手 ArcGIS:SketchEditorCtrl 地图开发实战”示例,从环境配置细节到核心代码拆解、流程图解、常见问题与优化建议,详细剖析了如何在 React Native 应用中集成 ArcGIS 地图,并使用 SketchEditorCtrl
完成“绘制多边形→编辑多边形→清除”整体流程。
- 环境准备:注册 ArcGIS 开发者账号,获取 API Key,配置 Android/iOS 原生文件。
- 核心组件:
ArcgisMapView
、SketchEditorCtrl
、GraphicLayerCtrl
三个 React Native 组件,以及GeometryType
、Graphic
等辅助类。 绘制与编辑流程:
startDraw(GeometryType.POLYGON)
进入绘制模式 → 用户点击地图绘制 →onDrawComplete(geometry)
回调。- 生成
Graphic
对象并添加到图层 → 地图渲染多边形。 loadGeometry(geometry)
进入编辑模式 →onVertexChanged(updatedGeometry)
实时回调 → 更新Graphic
。stopDraw()
停止绘制/编辑;removeAllGraphics()
清除全部。
- 核心代码示例:
App.js
提供完整示例,涵盖全部 API 调用、状态管理与 UI 交互。 - 图解流程:用简化顺序图分别说明“开始绘制→绘制完成→编辑→清除”的事件流与组件交互逻辑。
- 常见问题与优化:包括地图触摸无响应、多边形闭合、编辑闪烁、大数据量性能优化、GPS 定位结合、离线地图兼容等实战要点。
掌握以上技术后,你可以在 React Native 应用中实现更丰富的地图交互功能,如:
- 现场地理信息采集:用户可在户外通过手绘方式快速标记地物边界,并上传到后端云端。
- 资源规划与测量:结合 SketchEditor 提供面积、周长计算,实现地块测量与管理。
- 轨迹记录与回放:使用折线(
GeometryType.POLYLINE
)记录用户路径,并通过 timeStamp 字段实现地图轨迹回放。 - 复杂要素编辑:加载 CAD 转换的矢量要素,提供移动顶点、拉伸、剪切等编辑功能。
通过本文示例与思路,你应具备了在 React Native 环境下快速集成 ArcGIS 地图与 Sketch Editor 的能力。接下来,可以根据项目需求,自行扩展点、线、面的符号样式、测量工具、属性编辑器、图层管理、空间分析等功能,打造更为强大的 GIS 移动应用。
评论已关闭