# 🔧 几何计算工具 - 地图选点实现指南

## 📋 实现概述

本文档说明如何在几何计算工具中集成地图选点功能。

---

## ✅ 已完成的工作

### 1. 创建MapPointPicker组件

**文件位置：** `/components/field/MapPointPicker.tsx`

**功能：**
- ✅ 高德地图集成
- ✅ 多边形和单点两种模式
- ✅ 标记管理和多边形绘制
- ✅ 点击添加/删除坐标点
- ✅ 坐标列表显示

### 2. 更新状态管理

**在 FieldSpatialQuery.tsx 中添加：**

```typescript
// 地图选点模式
const [mapPickMode, setMapPickMode] = useState<'polygon' | 'distance-p1' | 'distance-p2' | null>(null);
const [editingPointIndex, setEditingPointIndex] = useState<number | null>(null);
```

### 3. 导入组件

```typescript
import { MapPointPicker } from './MapPointPicker';
import { MousePointer2, Edit3 } from 'lucide-react';
```

---

## 🔨 需要手动完成的集成

由于文件中存在重复的TabsContent结构，需要手动修改。以下是集成步骤：

### 步骤1: 修改面积计算TabsContent

**找到这部分代码（约第1470行）：**

```typescript
{/* 面积计算 */}
<TabsContent value="area" className="space-y-4">
  <Card className="p-6">
    <div className="flex items-center justify-between mb-4">
      <h3>多边形坐标点</h3>
      <Button size="sm" onClick={handleAddGeomPoint}>
        <Plus className="w-4 h-4 mr-2" />
        添加点
      </Button>
    </div>
    
    <div className="space-y-3 max-h-96 overflow-y-auto">
      {geomPoints.map((point, index) => (
        // ... 坐标输入框
      ))}
    </div>

    <Button onClick={handleCalculateArea} ...>
      计算面积 (ST_Area)
    </Button>
  </Card>
  
  {/* 结果显示 */}
</TabsContent>
```

**替换为：**

```typescript
{/* 面积计算 */}
<TabsContent value="area" className="space-y-4">
  {/* 选择方式切换 */}
  <div className="flex gap-2">
    <Button
      variant={mapPickMode === 'polygon' ? 'default' : 'outline'}
      onClick={() => setMapPickMode(mapPickMode === 'polygon' ? null : 'polygon')}
      className="flex-1"
    >
      <MousePointer2 className="w-4 h-4 mr-2" />
      {mapPickMode === 'polygon' ? '正在使用地图选点' : '地图选点'}
    </Button>
    <Button
      variant={mapPickMode === null ? 'default' : 'outline'}
      onClick={() => setMapPickMode(null)}
      className="flex-1"
    >
      <Edit3 className="w-4 h-4 mr-2" />
      手动输入坐标
    </Button>
  </div>

  {mapPickMode === 'polygon' ? (
    /* 地图选点模式 */
    <MapPointPicker
      points={geomPoints}
      mode="polygon"
      onPointsChange={setGeomPoints}
      height="400px"
      title="在地图上选择多边形顶点"
    />
  ) : (
    /* 手动输入模式 */
    <Card className="p-6">
      <div className="flex items-center justify-between mb-4">
        <h3>多边形坐标点</h3>
        <Button size="sm" onClick={handleAddGeomPoint}>
          <Plus className="w-4 h-4 mr-2" />
          添加点
        </Button>
      </div>
      
      <div className="space-y-3 max-h-96 overflow-y-auto">
        {geomPoints.map((point, index) => (
          <div key={index} className="flex items-center gap-2">
            <Badge variant="outline" className="w-16">
              点 {index + 1}
            </Badge>
            <Input
              type="number"
              step="0.000001"
              value={point.lat}
              onChange={(e) => handleUpdateGeomPoint(index, 'lat', e.target.value)}
              placeholder="纬度"
              className="flex-1"
            />
            <Input
              type="number"
              step="0.000001"
              value={point.lng}
              onChange={(e) => handleUpdateGeomPoint(index, 'lng', e.target.value)}
              placeholder="经度"
              className="flex-1"
            />
            <Button
              size="sm"
              variant="ghost"
              onClick={() => handleRemoveGeomPoint(index)}
              disabled={geomPoints.length <= 3}
            >
              <Trash2 className="w-4 h-4 text-red-500" />
            </Button>
          </div>
        ))}
      </div>
    </Card>
  )}

  <Button 
    onClick={handleCalculateArea}
    className="w-full bg-green-600 hover:bg-green-700"
  >
    <Calculator className="w-4 h-4 mr-2" />
    计算面积 (ST_Area)
  </Button>
  
  {/* 结果显示部分保持不变 */}
  {geomResult && geomResult.type === 'area' && (
    // ... 原有的结果显示代码
  )}
</TabsContent>
```

### 步骤2: 修改周长计算TabsContent

使用相同的模式修改周长计算部分（约第1550行）

### 步骤3: 修改中心点计算TabsContent

使用相同的模式修改中心点计算部分（约第1610行）

### 步骤4: 修改包围盒计算TabsContent

使用相同的模式修改包围盒计算部分（最后一个多边形计算）

### 步骤5: 修改距离计算TabsContent

**找到距离计算部分：**

```typescript
{/* 距离计算 */}
<TabsContent value="distance" className="space-y-4">
  <Card className="p-6">
    <h3 className="mb-4">两点坐标</h3>
    
    <div className="space-y-4">
      <div>
        <div className="mb-2 font-medium">点1坐标</div>
        <div className="grid grid-cols-2 gap-3">
          <div>
            <label className="text-sm text-muted-foreground">纬度</label>
            <Input ... />
          </div>
          <div>
            <label className="text-sm text-muted-foreground">经度</label>
            <Input ... />
          </div>
        </div>
      </div>
      
      {/* 点2坐标 - 类似结构 */}
    </div>
  </Card>
</TabsContent>
```

**替换为：**

```typescript
{/* 距离计算 */}
<TabsContent value="distance" className="space-y-4">
  {/* 点1选择 */}
  <Card className="p-6">
    <h3 className="mb-4">点1坐标</h3>
    
    {/* 切换按钮 */}
    <div className="flex gap-2 mb-4">
      <Button
        variant={mapPickMode === 'distance-p1' ? 'default' : 'outline'}
        onClick={() => setMapPickMode(mapPickMode === 'distance-p1' ? null : 'distance-p1')}
        className="flex-1"
      >
        <MousePointer2 className="w-4 h-4 mr-2" />
        {mapPickMode === 'distance-p1' ? '正在使用地图选点' : '地图选点'}
      </Button>
      <Button
        variant={mapPickMode === null || mapPickMode === 'distance-p2' ? 'default' : 'outline'}
        onClick={() => setMapPickMode(null)}
        className="flex-1"
      >
        <Edit3 className="w-4 h-4 mr-2" />
        手动输入坐标
      </Button>
    </div>

    {mapPickMode === 'distance-p1' ? (
      <MapPointPicker
        points={[distPoint1]}
        mode="single"
        onPointsChange={(points) => setDistPoint1(points[0] || distPoint1)}
        height="300px"
        title="在地图上选择点1位置"
      />
    ) : (
      <div className="grid grid-cols-2 gap-3">
        <div>
          <label className="text-sm text-muted-foreground">纬度</label>
          <Input
            type="number"
            step="0.000001"
            value={distPoint1.lat}
            onChange={(e) => setDistPoint1({ ...distPoint1, lat: parseFloat(e.target.value) || 0 })}
          />
        </div>
        <div>
          <label className="text-sm text-muted-foreground">经度</label>
          <Input
            type="number"
            step="0.000001"
            value={distPoint1.lng}
            onChange={(e) => setDistPoint1({ ...distPoint1, lng: parseFloat(e.target.value) || 0 })}
          />
        </div>
      </div>
    )}
  </Card>

  {/* 点2选择 - 类似结构，使用 mapPickMode === 'distance-p2' 和 distPoint2 */}
  <Card className="p-6">
    <h3 className="mb-4">点2坐标</h3>
    
    <div className="flex gap-2 mb-4">
      <Button
        variant={mapPickMode === 'distance-p2' ? 'default' : 'outline'}
        onClick={() => setMapPickMode(mapPickMode === 'distance-p2' ? null : 'distance-p2')}
        className="flex-1"
      >
        <MousePointer2 className="w-4 h-4 mr-2" />
        {mapPickMode === 'distance-p2' ? '正在使用地图选点' : '地图选点'}
      </Button>
      <Button
        variant={mapPickMode === null || mapPickMode === 'distance-p1' ? 'default' : 'outline'}
        onClick={() => setMapPickMode(null)}
        className="flex-1"
      >
        <Edit3 className="w-4 h-4 mr-2" />
        手动输入坐标
      </Button>
    </div>

    {mapPickMode === 'distance-p2' ? (
      <MapPointPicker
        points={[distPoint2]}
        mode="single"
        onPointsChange={(points) => setDistPoint2(points[0] || distPoint2)}
        height="300px"
        title="在地图上选择点2位置"
      />
    ) : (
      <div className="grid grid-cols-2 gap-3">
        <div>
          <label className="text-sm text-muted-foreground">纬度</label>
          <Input
            type="number"
            step="0.000001"
            value={distPoint2.lat}
            onChange={(e) => setDistPoint2({ ...distPoint2, lat: parseFloat(e.target.value) || 0 })}
          />
        </div>
        <div>
          <label className="text-sm text-muted-foreground">经度</label>
          <Input
            type="number"
            step="0.000001"
            value={distPoint2.lng}
            onChange={(e) => setDistPoint2({ ...distPoint2, lng: parseFloat(e.target.value) || 0 })}
          />
        </div>
      </div>
    )}
  </Card>

  <Button 
    onClick={handleCalculateDistance}
    className="w-full bg-orange-600 hover:bg-orange-700"
  >
    <MapIcon className="w-4 h-4 mr-2" />
    计算距离 (ST_Distance)
  </Button>
  
  {/* 结果显示部分保持不变 */}
</TabsContent>
```

---

## 📝 修改清单

需要修改的5个TabsContent：

- [ ] **面积计算** (area) - 多边形模式
- [ ] **周长计算** (perimeter) - 多边形模式
- [ ] **中心点计算** (centroid) - 多边形模式
- [ ] **包围盒计算** (bbox) - 多边形模式
- [ ] **距离计算** (distance) - 双点模式

---

## 🎯 关键要点

### 1. 模式切换按钮

```typescript
<Button
  variant={mapPickMode === 'polygon' ? 'default' : 'outline'}
  onClick={() => setMapPickMode(mapPickMode === 'polygon' ? null : 'polygon')}
>
  地图选点
</Button>
```

### 2. 条件渲染

```typescript
{mapPickMode === 'polygon' ? (
  <MapPointPicker ... />
) : (
  <Card>手动输入</Card>
)}
```

### 3. MapPointPicker属性

```typescript
<MapPointPicker
  points={geomPoints}           // 坐标数组
  mode="polygon"                 // 或 "single"
  onPointsChange={setGeomPoints} // 更新回调
  height="400px"                 // 地图高度
  title="选择坐标点"             // 标题
/>
```

---

## ✅ 测试检查

完成集成后，测试以下功能：

### 面积计算
- [ ] 切换到地图选点模式
- [ ] 在地图上点击添加4个点
- [ ] 验证多边形自动绘制
- [ ] 点击标记删除点
- [ ] 切换回手动输入模式
- [ ] 验证坐标保留
- [ ] 计算面积成功

### 周长计算
- [ ] 使用相同测试流程

### 中心点计算
- [ ] 使用相同测试流程

### 距离计算
- [ ] 点1使用地图选择
- [ ] 点2使用地图选择
- [ ] 验证两点标记显示
- [ ] 计算距离成功

### 包围盒计算
- [ ] 使用相同测试流程

---

## 🐛 可能的问题

### 问题1: 地图未显示

**原因：** 高德地图SDK未加载
**解决：** 检查mapLoader.ts是否正常工作

### 问题2: 点击地图无反应

**原因：** 事件监听未生效
**解决：** 检查MapPointPicker组件的useEffect

### 问题3: 标记位置不准

**原因：** 经纬度顺序错误
**解决：** 高德地图使用[lng, lat]顺序

### 问题4: 多边形未绘制

**原因：** 点数不足3个
**解决：** 至少添加3个点才能形成多边形

---

## 🎉 完成后的效果

用户可以：

1. **灵活选择输入方式**
   - 地图直观选点
   - 手动精确输入
   - 随时切换

2. **实时可视化反馈**
   - 标记即时显示
   - 多边形自动绘制
   - 坐标实时更新

3. **便捷操作**
   - 点击添加
   - 点击删除
   - 一键清除

4. **保留原有功能**
   - 所有计算功能不变
   - 精度保持不变
   - 结果显示不变

---

## 📚 相关文件

- `/components/field/MapPointPicker.tsx` - 地图选点组件
- `/components/field/FieldSpatialQuery.tsx` - 空间数据管理主组件
- `/lib/spatialDataService.ts` - 空间计算服务
- `/lib/mapLoader.ts` - 地图SDK加载器

---

**创建日期：** 2025-10-18  
**版本：** v4.0  
**状态：** 组件已创建，需手动集成  
**维护团队：** 智慧农业研发中心
