@wenle_2523097/agri-map
v2.0.2
Published
农业地图组件库 - 基于 Leaflet + React 的农业专用地图组件
Maintainers
Readme
agri-map-components
基于 Leaflet + React 的农业专用地图组件库,提供地块管理、标注点、道路、灌溉渠等农业场景常用组件。
特性
- 🗺️ 地块管理 - 支持大规模地块数据渲染,提供绘制、剪辑、移动等编辑功能
- 📍 标注点 - 多种预设图标,支持自定义 SVG 图标,提供新建、移动、删除编辑模式
- 🛤️ 道路绘制 - 田间道路绘制与管理,支持距离显示
- 💧 灌溉渠 - 灌溉渠绘制与流向箭头显示
- 🌍 天地图 - 内置天地图图层支持
- 📐 双向标尺 - 地图比例尺组件
- ⚡ 高性能 - 支持上万条地块数据渲染
- 🛡️ 错误边界 - 内置 ErrorBoundary 组件,优雅处理渲染错误
- 📊 性能监控 - 提供 usePerformanceMonitor Hook 监控组件性能
安装
npm install @wenle_2523097/agri-map
# 或
yarn add @wenle_2523097/agri-map依赖版本
确保项目已安装以下依赖:
npm install leaflet@^1.9.4 react@>=18.0.0 react-dom@>=18.0.0⚠️ 重要提示:
antd为 peerDependency,按需安装:npm install antd@>=5.0.0- 本库已移除
react-leaflet依赖,直接使用原生 Leaflet API,更轻量、更灵活
样式引入
组件库的 CSS 样式会自动注入到 DOM 中,无需手动引入。
// 只需引入 Leaflet 的基础样式即可
import 'leaflet/dist/leaflet.css';
// ✨ 组件库样式已自动注入,以下代码不再需要:
// import '@wenle_2523097/agri-map/dist/index.css'; // ❌ 已废弃Breaking Change: 从 v1.4.0 开始,CSS 样式改为自动注入。如果你之前手动引入了
dist/index.css,请删除该导入,避免重复加载。
快速开始
// 引入 Leaflet 样式
import 'leaflet/dist/leaflet.css';
// ✨ CSS 已自动注入,无需引入
import { MapContainer, TianDiTuLayer, PlotLayer, Marker } from '@wenle_2523097/agri-map';
function App() {
const plotData = [
{
id: 1,
name: '地块A',
type: 'farmland',
status: 'planting',
boundaries: [[30.0, 120.0], [30.0, 120.1], [30.1, 120.1], [30.1, 120.0]],
area: 1000,
crop: '水稻',
},
];
const markers = [
{ id: 1, position: [30.05, 120.05], title: '标注点1' },
];
return (
<div style={{ width: '100%', height: '100vh' }}>
<MapContainer
center={[30.0, 120.0]}
zoom={12}
theme="light"
tianDiTuLayer={{ apiKey: 'your-tianditu-key' }}
dualScaleControl
>
<PlotLayer dataSource={plotData} />
<Marker dataSource={markers} />
</MapContainer>
</div>
);
}组件列表
PlotLayer 地块图层
高性能地块渲染组件,支持大规模数据。
import { PlotLayer, PlotData } from 'agri-map';
const plots: PlotData[] = [
{
id: 'plot-1',
name: '农田A',
type: 'farmland',
status: 'planting',
boundaries: [[30.0, 120.0], [30.0, 120.1], [30.1, 120.1], [30.1, 120.0]],
area: 1000,
crop: '水稻',
},
];
<PlotLayer
dataSource={plots}
onClick={(data, layer) => console.log('点击地块:', data)}
onContextMenu={(data, layer, e) => console.log('右键菜单:', data)}
style={(data) => ({
color: data.status === 'planting' ? '#4CAF50' : '#9E9E9E',
fillColor: data.status === 'planting' ? '#81C784' : '#BDBDBD',
fillOpacity: 0.6,
})}
editMode={['create', 'delete']}
onCreate={(result) => console.log('新建地块:', result)}
onDelete={(result) => console.log('删除地块:', result)}
showLabel={true}
labelFields={['name', 'area']}
/>PlotLayer Props
| 属性 | 类型 | 默认值 | 说明 |
| --------------- | ------------------------------ | ---------- | ------------ |
| dataSource | PlotData[] | [] | 地块数据数组 |
| onClick | (data, layer) => void | - | 点击回调 |
| onContextMenu | (data, layer, event) => void | - | 右键菜单回调 |
| style | (data) => PlotStyle | - | 动态样式函数 |
| path | PathOptions | - | 默认路径样式 |
| selectedPath | PathOptions | - | 选中样式 |
| editMode | boolean \| string[] | false | 编辑模式 |
| onCreate | (result) => void | - | 新建回调 |
| onRedraw | (result) => void | - | 重绘回调 |
| onClip | (result) => void | - | 剪辑回调 |
| onMove | (result) => void | - | 移动回调 |
| onDelete | (result) => void | - | 删除回调 |
| showLabel | boolean \| 'all' | false | 显示标签 |
| labelFields | string[] | ['name'] | 标签字段 |
Marker 标注点
地图标注点组件,支持多种图标和编辑功能。
import { Marker, MarkerData } from 'agri-map';
const markers: MarkerData[] = [
{ id: 1, position: [30.0, 120.0], title: '标注点1', description: '描述信息' },
{ id: 2, position: [30.1, 120.1], title: '标注点2', icon: 'drone' },
];
<Marker
dataSource={markers}
icon="point"
iconSize={[48, 48]}
animated={false}
onClick={(item) => console.log('点击:', item)}
editMode={['create', 'move', 'delete']}
onCreate={(result) => console.log('新建:', result)}
/>预设图标
point- 默认点位图标position- 定位图标position-red- 红色定位图标drone- 无人机图标down- 下载图标marker-red/blue/green/orange/yellow- 彩色标记图标
自定义 SVG 图标
// 使用 SVG 字符串
<Marker svgIcon="<svg>...</svg>" />
// 使用 SVG 配置对象
<Marker svgIcon={{
path: 'M12 2C8.13 2 5 5.13 5 9c0 5.25 7 13 7 13s7-7.75 7-13c0-3.87-3.13-7-7-7z',
viewBox: '0 0 24 24',
fill: '#e53935',
}} />
// 使用远程图标 URL
<Marker icon="https://example.com/icon.png" />Marker Props
| 属性 | 类型 | 默认值 | 说明 |
| ---------------- | -------------------------------- | ---------- | ---------------- |
| dataSource | MarkerData[] | [] | 标注数据 |
| icon | string \| SvgIconConfig | 'point' | 图标类型 |
| iconSize | [number, number] | [48, 48] | 图标尺寸 |
| animated | boolean | false | 是否动画 |
| permanent | boolean | false | 永久显示提示 |
| editMode | boolean \| string[] | false | 编辑模式 |
| onCreate | (result) => void | - | 新建回调 |
| onMove | (result) => void | - | 移动回调 |
| onDelete | (result) => void | - | 删除回调 |
| cluster | boolean \| MarkerClusterConfig | false | 聚合配置 |
| onClusterClick | (count, markers) => void | - | 聚合气泡点击回调 |
| icons | MarkerIconsConfig | - | 统一图标配置 |
聚合功能
Marker 组件支持标注点聚合,在小缩放级别下将相近的标注点聚合为气泡显示,提升大量标注点时的渲染性能。
// 启用默认聚合
<Marker
dataSource={markers}
cluster={true}
/>
// 自定义聚合配置
<Marker
dataSource={markers}
cluster={{
radius: 60, // 聚合半径(像素),默认 40
maxZoom: 16, // 最大聚合 zoom 级别,默认 18
minPoints: 3, // 最小聚合点数,默认 2
onClickBehavior: 'expand', // 点击行为:'zoom' 或 'expand'
}}
onClusterClick={(count, markers) => {
console.log(`点击了包含 ${count} 个点的聚合`);
}}
/>
// 自定义聚合气泡图标
<Marker
dataSource={markers}
cluster={true}
icons={{
cluster: {
icon: 'marker-yellow',
colors: ['#1890ff', '#faad14', '#ff4d4f'],
animated: true,
showCount: true,
},
default: { icon: 'point', iconSize: [32, 32] },
selected: { icon: 'position-red' },
}}
/>MarkerClusterConfig
| 属性 | 类型 | 默认值 | 说明 |
| ----------------- | -------------------- | -------- | ------------------------ |
| radius | number | 40 | 聚合半径(像素) |
| maxZoom | number | 18 | 聚合生效的最大 zoom 级别 |
| minPoints | number | 2 | 最小聚合点数 |
| onClickBehavior | 'zoom' \| 'expand' | 'zoom' | 点击聚合气泡后的行为 |
MarkerIconsConfig
| 属性 | 类型 | 说明 |
| ---------- | ---------------------- | ------------------ |
| default | MarkerIconItemConfig | 默认状态图标配置 |
| selected | MarkerIconItemConfig | 选中状态图标配置 |
| editing | MarkerIconItemConfig | 编辑中状态图标配置 |
| cluster | ClusterIconConfig | 聚合气泡图标配置 |
Road 道路组件
田间道路绘制与管理组件。
import { Road, RoadData } from 'agri-map';
const roads: RoadData[] = [
{
id: 1,
name: '田间道1号',
points: [[30.0, 120.0], [30.1, 120.1], [30.2, 120.2]],
},
];
<Road
dataSource={roads}
path={{ color: '#795548', weight: 3 }}
showDistance={true}
editMode={['create', 'edit', 'delete']}
onCreate={(result) => console.log('新建道路:', result)}
/>Road Props
| 属性 | 类型 | 默认值 | 说明 |
| -------------- | --------------------- | ------- | -------- |
| dataSource | RoadData[] | [] | 道路数据 |
| path | PathOptions | - | 路径样式 |
| showDistance | boolean | false | 显示距离 |
| editMode | boolean \| string[] | false | 编辑模式 |
Irrigation 灌溉渠组件
灌溉渠绘制与流向显示组件。
import { Irrigation, IrrigationData } from 'agri-map';
const channels: IrrigationData[] = [
{
id: 1,
name: '主干渠',
points: [[30.0, 120.0], [30.1, 120.1]],
inlet: true,
outlet: false,
},
];
<Irrigation
dataSource={channels}
path={{ color: '#2196F3', weight: 4 }}
editMode={['create', 'edit', 'delete', 'reverse']}
onCreate={(result) => console.log('新建渠道:', result)}
onReverse={(result) => console.log('反转流向:', result)}
/>Irrigation Props
| 属性 | 类型 | 默认值 | 说明 |
| ------------ | --------------------- | ------- | ------------ |
| dataSource | IrrigationData[] | [] | 灌溉渠数据 |
| path | PathOptions | - | 路径样式 |
| editMode | boolean \| string[] | false | 编辑模式 |
| onReverse | (result) => void | - | 反转流向回调 |
TianDiTuLayer 天地图图层
天地图底图组件。
import { TianDiTuLayer, LAYER_TYPES } from 'agri-map';
// 矢量底图 + 标注
<TianDiTuLayer
apiKey="your-tianditu-key"
layerType="vector"
showAnnotation={true}
/>
// 卫星影像
<TianDiTuLayer
apiKey="your-tianditu-key"
layerType="satellite"
/>
// 地形图
<TianDiTuLayer
apiKey="your-tianditu-key"
layerType="terrain"
/>
// 自定义叠加层
<TianDiTuLayer
apiKey="your-tianditu-key"
overlayUrl="https://your-server.com/overlay/{z}/{x}/{y}.png"
overlayOpacity={0.5}
/>TianDiTuLayer Props
| 属性 | 类型 | 默认值 | 说明 |
| ---------------- | -------------------------------------- | ---------- | ---------------------- |
| apiKey | string | - | 天地图 API Key(必填) |
| layerType | 'vector' \| 'satellite' \| 'terrain' | 'vector' | 图层类型 |
| showAnnotation | boolean | true | 显示标注 |
| overlayUrl | string | - | 叠加瓦片 URL |
| overlayOpacity | number | 1 | 叠加层透明度 |
DualScaleControl 双向标尺
地图比例尺组件,缩放控件固定在左下角。
import { DualScaleControl } from 'agri-map';
<DualScaleControl
metric={true}
imperial={false}
showZoomControl={true}
theme="light"
/>ErrorBoundary 错误边界
捕获子组件树中的 JavaScript 错误,记录错误信息,并展示回退 UI。
import { ErrorBoundary } from 'agri-map';
// 基础用法
<ErrorBoundary>
<MapContainer>
<PlotLayer dataSource={plots} />
</MapContainer>
</ErrorBoundary>
// 自定义回退 UI
<ErrorBoundary
fallback={(error, reset) => (
<div>
<p>出错了:{error.message}</p>
<button onClick={reset}>重试</button>
</div>
)}
>
<YourComponent />
</ErrorBoundary>
// 带错误回调
<ErrorBoundary
onError={(error, errorInfo) => {
// 上报错误到监控服务
logErrorToService(error, errorInfo);
}}
>
<YourComponent />
</ErrorBoundary>ErrorBoundary Props
| 属性 | 类型 | 默认值 | 说明 |
| ------------------------- | ------------------------------------------ | ------- | -------------------- |
| fallback | ReactNode \| (error, reset) => ReactNode | - | 错误回退 UI |
| onError | (error, errorInfo) => void | - | 错误回调 |
| showDetailsInProduction | boolean | false | 生产环境显示错误详情 |
usePerformanceMonitor 性能监控 Hook
监控组件渲染性能和地图操作性能。
import { usePerformanceMonitor } from 'agri-map/hooks';
function PlotLayer({ dataSource }) {
const {
metrics,
recordRenderPerformance,
observeMapOperation,
reportPerformance
} = usePerformanceMonitor('PlotLayer', {
enabled: true,
reportToService: process.env.NODE_ENV === 'production',
serviceUrl: '/api/performance',
});
useEffect(() => {
console.log('渲染性能:', metrics);
}, [metrics]);
return <div>...</div>;
}PerformanceMonitorConfig
| 属性 | 类型 | 默认值 | 说明 |
| ----------------- | ------------------------- | ------- | ------------------ |
| enabled | boolean | true | 是否启用监控 |
| reportToService | boolean | false | 是否上报到监控服务 |
| serviceUrl | string | - | 监控服务上报 URL |
| extraData | Record<string, unknown> | - | 额外数据 |
返回值
| 属性 | 类型 | 说明 |
| ------------------------- | -------------------------- | ---------------- |
| metrics | PerformanceMetrics | 性能指标数据 |
| getMetrics | () => PerformanceMetrics | 获取完整性能指标 |
| recordRenderPerformance | (type) => void | 记录渲染性能 |
| observeMapOperation | (map, name, op) => T | 观察地图操作性能 |
| reportPerformance | () => void | 上报性能数据 |
MapContainer 地图容器
集成配置功能的地图容器组件,替代了原有的 ConfigProvider + react-leaflet MapContainer 组合。
import { MapContainer } from '@wenle_2523097/agri-map';
<MapContainer
center={[30.0, 120.0]}
zoom={12}
theme="light" // 主题:'light' | 'dark'
tianDiTuLayer={{ // 天地图配置
apiKey: 'your-tianditu-key',
layerType: 'vector',
showAnnotation: true,
}}
dualScaleControl // 显示双向标尺
mapSizeHandler // 自动处理地图尺寸变化
>
{/* 子组件 */}
</MapContainer>MapContainer Props
| 属性 | 类型 | 默认值 | 说明 |
| ------------------ | ---------------------------------- | --------- | -------------------- |
| center | [number, number] | - | 地图中心点(必填) |
| zoom | number | - | 缩放级别(必填) |
| theme | 'light' \| 'dark' | 'light' | 主题 |
| tianDiTuLayer | TianDiTuLayerConfig \| false | - | 天地图配置 |
| dualScaleControl | boolean \| DualScaleControlProps | false | 双向标尺配置 |
| mapSizeHandler | boolean | true | 自动处理地图尺寸变化 |
| loading | boolean | false | 显示加载状态 |
| children | ReactNode | - | 子组件 |
注意:
MapContainer同时支持所有原生 Leaflet 地图选项(如minZoom,maxZoom,zoomControl等)。
useMap Hook
获取地图实例的 Hook,用于在子组件中操作地图。API 与 react-leaflet 的 useMap 保持一致。
import { useMap } from '@wenle_2523097/agri-map';
function MyComponent() {
const map = useMap();
useEffect(() => {
if (map) {
map.setView([30.0, 120.0], 15);
}
}, [map]);
return null;
}useMapEvents Hook
同时监听多个地图事件的 Hook,API 与 react-leaflet 的 useMapEvents 保持一致。返回地图实例。
import { useMapEvents } from '@wenle_2523097/agri-map';
function MyComponent() {
const map = useMapEvents({
click: (e) => {
console.log('点击位置:', e.latlng);
},
zoomend: () => {
console.log('当前缩放级别:', map?.getZoom());
},
moveend: () => {
console.log('当前中心点:', map?.getCenter());
},
});
return null;
}Icons 图标组件
内置 SVG 图标组件库。
import {
Icons,
PlotRedrawIcon,
ClipIcon,
MoveIcon,
DeleteIcon,
CancelIcon,
SaveIcon,
CreateIcon
} from 'agri-map';
// 使用方式一
<CreateIcon />
// 使用方式二
<Icons.Create />类型定义
// 坐标点 [lat, lng]
type Coordinate = [number, number];
// 地块类型
type PlotType = 'farmland' | 'vegetable' | 'orchard' | 'forest' | 'grassland' | 'paddy' | 'greenhouse';
// 地块状态
type PlotStatus = 'cultivating' | 'fallow' | 'harvesting' | 'planting' | 'irrigating';
// 编辑模式
type PlotEditMode = 'idle' | 'redraw' | 'clip' | 'move' | 'create';
// 路径样式
interface PathOptions {
color?: string;
fillColor?: string;
weight?: number;
opacity?: number;
fillOpacity?: number;
dashArray?: string | number[];
className?: string;
}开发
# 安装依赖
npm install
# 开发模式
npm run dev
# 构建
npm run build
# 类型检查
npm run type-check许可证
MIT
