地图缩放控制是 SunvStation 二次开发中的核心功能之一，它允许脚本程序动态调整地图视图的显示范围、比例尺和视角。通过精确的缩放控制，可以实现自动定位、视图切换、数据浏览等丰富的交互功能。本页将系统地介绍地图缩放控制的各项功能、使用方法和最佳实践。

Sources: [PySSMap.py](PySSMap.py#L303-L3440), [PySSView.py](PySSView.py#L1000-L1362)

## 架构概览

SunvStation 的地图缩放控制采用分层架构设计，底层的 `ScaleMap` 对象负责地图数据的逻辑管理，顶层的 `VkPainter2D` 或 `VkPainter3D` 对象负责视图的渲染和交互。以下展示了地图缩放控制的调用层次结构：

```mermaid
graph TB
    A[Python 脚本] -->|调用| B[SSProcess 实例]
    B -->|继承| C[ProjectMixin]
    C -->|提供方法| D[getCurrentMap]
    D -->|返回| SM[ScaleMap 地图对象]
    
    SM -->|包含| VL[ViewList 视图列表]
    VL -->|包含| VP2[VkPainter2D 2D视图]
    VL -->|包含| VP3[VkPainter3D 3D视图]
    
    SM -->|提供| MS[map_scale: 比例尺控制]
    SM -->|提供| MR[getMapRect: 地图矩形]
    SM -->|提供| ZM[zoom: 对象缩放]
    
    VP2 -->|提供| ZW[zoomWindow: 窗口缩放]
    VP2 -->|提供| MP[moveScreenByPoint: 平移]
    VP2 -->|提供| GS[getImgScale: 获取缩放比例]
    VP2 -->|提供| CTS[坐标转换方法组]
    VP2 -->|提供| LC[长度转换方法组]
    
    VP3 -->|提供| ZW2[zoomWindow: 窗口缩放]
    VP3 -->|提供| MP2[moveScreenByPoint: 平移]
    VP3 -->|提供| GS2[getImgScale: 获取缩放比例]
    VP3 -->|提供| CTS2[坐标转换方法组]
    VP3 -->|提供| LC2[长度转换方法组]
    
    style B fill:#e1f5ff
    style C fill:#fff4e1
    style SM fill:#f0f0f0
    style VP2 fill:#e8f5e9
    style VP3 fill:#e8f5e9
```

上图展示了地图缩放控制的双层架构。`ScaleMap` 对象管理地图数据和比例尺，通过 `setMapScale()` 和 `getMapScale()` 方法实现比例尺的设置和查询。`VkPainter2D` 和 `VkPainter3D` 对象负责视图渲染，提供了窗口缩放、平移、坐标转换等视图操作方法。

Sources: [PySSMap.py](PySSMap.py#L303-L3440), [PySSView.py](PySSView.py#L950-L1362), [PySSProcess.py](PySSProcess.py#L25-L70)

## 地图比例尺控制

地图比例尺是地图缩放控制的核心参数，它决定了地图上地理要素的显示大小。SunvStation 提供了直接设置和查询地图比例尺的方法，适用于需要精确控制显示比例的场景。

### 比例尺控制方法

| 方法 | 功能描述 | 返回值 |
|-----|---------|--------|
| `map.setMapScale(lScale)` | 设置地图比例尺 | boolean |
| `map.getMapScale()` | 获取当前地图比例尺 | long |

**参数说明**：

- `lScale`：比例尺分母，例如 500 表示 1:500 的比例尺

Sources: [PySSMap.py](PySSMap.py#L2330-L2335), [PySSMap.py](PySSMap.py#L2430-L2432)

### 设置比例尺流程

```mermaid
flowchart LR
    A[开始设置比例尺] --> B[获取当前地图]
    B -->|map| C[调用 setMapScale]
    C -->|传入 lScale| D{设置是否成功}
    D -->|成功| E[比例尺已更新]
    D -->|失败| F[检查参数有效性]
    F --> G[重新调用 setMapScale]
    E --> H[调用 repaint 更新视图]
    H --> I[完成]
    
    style A fill:#e1f5ff
    style D fill:#fff4e1
    style H fill:#ffe8e1
```

### 使用示例

```python
from sunvpy import SSProcess

# 获取当前地图
current_map = SSProcess.getCurrentMap()

# 获取当前比例尺
current_scale = current_map.getMapScale()
print(f"当前比例尺: 1:{current_scale}")

# 设置新比例尺为 1:1000
new_scale = 1000
success = current_map.setMapScale(new_scale)
if success:
    print(f"比例尺已设置为: 1:{new_scale}")
else:
    print("比例尺设置失败")
```

Sources: [PySSMap.py](PySSMap.py#L2330-L2335), [PySSMap.py](PySSMap.py#L2430-L2432), [ssprocess_mixins/project_mixin.py](ssprocess_mixins/project_mixin.py#L45-L50)

## 窗口缩放控制

窗口缩放是指通过指定矩形区域来调整地图视图的显示范围，这是实现快速定位和聚焦查看特定区域的有效方法。

### 窗口缩放方法

| 方法 | 功能描述 | 适用视图 |
|-----|---------|---------|
| `painter.zoomWindow(rect, z)` | 缩放到指定矩形区域 | VkPainter2D, VkPainter3D |

**参数说明**：

- `rect`：目标矩形区域（`DblRect` 类型），定义了缩放后的显示范围
- `z`：缩放因子，控制缩放程度

Sources: [PySSView.py](PySSView.py#L1030-L1032), [PySSView.py](PySSView.py#L1154-L1156)

### 窗口缩放流程

```mermaid
sequenceDiagram
    participant Script as Python 脚本
    participant Map as ScaleMap
    participant Painter as VkPainter2D/3D
    
    Script->>Map: getCurrentMap()
    Map-->>Script: 返回地图对象
    
    Script->>Painter: 获取视图对象
    Painter-->>Script: 返回 VkPainter 实例
    
    Script->>Painter: zoomWindow(rect, z)
    Painter->>Painter: 计算新的视图范围
    Painter->>Painter: 调整摄像机参数
    Painter->>Painter: 重新渲染场景
    Painter-->>Script: 返回操作结果
    
    Script->>Painter: repaint()
    Painter->>Painter: 刷新显示
```

### 使用示例

```python
from sunvpy import SSProcess
from sunvpy.PySSMath import DblRect

# 获取当前地图
current_map = SSProcess.getCurrentMap()

# 假设已获取视图对象（实际使用时需根据具体情况获取）
# 这里展示窗口缩放的逻辑
rect = DblRect()  # 创建矩形对象
# 设置矩形的范围（示例值）
# rect.set(...) 

# 缩放到指定窗口区域
zoom_factor = 1.0  # 缩放因子
# painter.zoomWindow(rect, zoom_factor)

# 刷新视图
# painter.repaint()
```

Sources: [PySSView.py](PySSView.py#L1030-L1032), [PySSView.py](PySSView.py#L1154-L1156), [PySSView.py](PySSView.py#L900-L910)

## 坐标系统转换

坐标系统转换是地图缩放控制中的关键功能，它实现了地理坐标（地图空间）与屏幕坐标（显示空间）之间的双向转换。这对于鼠标交互、定位标注等场景至关重要。

### 坐标转换方法表

| 方法 | 功能描述 | 输入 | 输出 |
|-----|---------|------|------|
| `painter.geoToScreen(point)` | 地理坐标转屏幕坐标 | Point3D | Point2D |
| `painter.screenToGeo(point)` | 屏幕坐标转地理坐标 | Point2D | Point3D |
| `painter.lengthToPixels(dGeoLength)` | 地理长度转像素 | float（米） | int（像素） |
| `painter.pixelsToLength(nPixels)` | 像素转地理长度 | int（像素） | float（米） |

Sources: [PySSView.py](PySSView.py#L1034-L1041), [PySSView.py](PySSView.py#L1158-L1165)

### 坐标转换流程

```mermaid
flowchart LR
    subgraph "地理坐标到屏幕坐标"
        A1[Point3D 地理坐标] -->|geoToScreen| B1[Point2D 屏幕坐标]
    end
    
    subgraph "屏幕坐标到地理坐标"
        A2[Point2D 屏幕坐标] -->|screenToGeo| B2[Point3D 地理坐标]
    end
    
    subgraph "长度单位转换"
        A3[float 地理长度] -->|lengthToPixels| B3[int 像素值]
        A4[int 像素值] -->|pixelsToLength| B4[float 地理长度]
    end
    
    style A1 fill:#e1f5ff
    style A2 fill:#e1f5ff
    style A3 fill:#fff4e1
    style A4 fill:#fff4e1
```

### 使用示例

```python
from sunvpy import SSProcess
from sunvpy.PySSMath import Point3D, Point2D

# 获取当前地图
current_map = SSProcess.getCurrentMap()

# 示例：地理坐标转屏幕坐标
geo_point = Point3D(500000.0, 3000000.0, 0.0)  # 地理坐标
# screen_point = painter.geoToScreen(geo_point)
# print(f"屏幕坐标: ({screen_point.x()}, {screen_point.y()})")

# 示例：屏幕坐标转地理坐标
screen_point = Point2D(100, 200)  # 屏幕坐标（像素）
# geo_point = painter.screenToGeo(screen_point)
# print(f"地理坐标: ({geo_point.x()}, {geo_point.y()}, {geo_point.z()})")

# 示例：地理长度转像素
geo_length = 50.0  # 50米
# pixels = painter.lengthToPixels(geo_length)
# print(f"{geo_length}米 = {pixels}像素")

# 示例：像素转地理长度
pixels = 100  # 100像素
# geo_length = painter.pixelsToLength(pixels)
# print(f"{pixels}像素 = {geo_length}米")
```

Sources: [PySSView.py](PySSView.py#L1034-L1041), [PySSView.py](PySSView.py#L1158-L1165)

## 视图平移控制

视图平移是指在不改变比例尺的情况下，调整地图视图的中心位置，实现地图的漫游浏览效果。

### 平移控制方法

| 方法 | 功能描述 | 参数说明 |
|-----|---------|---------|
| `painter.moveScreenByPoint(refPoint, bMoveNoIf)` | 按参考点平移视图 | `refPoint`：参考点坐标<br>`bMoveNoIf`：是否在未移动时跳过 |

Sources: [PySSView.py](PySSView.py#L1031-L1033), [PySSView.py](PySSView.py#L1155-L1157)

### 平移流程

```mermaid
flowchart LR
    A[开始平移] --> B[获取参考点坐标]
    B -->|Point3D| C[调用 moveScreenByPoint]
    C -->|refPoint, bMoveNoIf| D{需要平移吗?}
    D -->|是| E[计算视图偏移量]
    E --> F[更新视图中心点]
    F --> G[重新渲染场景]
    D -->|否| H[跳过操作]
    G --> I[完成]
    H --> I
    
    style A fill:#e1f5ff
    style D fill:#fff4e1
    style G fill:#ffe8e1
```

### 使用示例

```python
from sunvpy import SSProcess
from sunvpy.PySSMath import Point3D

# 获取当前地图
current_map = SSProcess.getCurrentMap()

# 定义平移参考点
ref_point = Point3D(500010.0, 3000010.0, 0.0)

# 执行平移操作（假设已获取 painter）
# success = painter.moveScreenByPoint(ref_point, False)
# if success:
#     print("平移成功")
#     # 刷新视图
#     painter.repaint()
```

Sources: [PySSView.py](PySSView.py#L1031-L1033), [PySSView.py](PySSView.py#L1155-L1157)

## 获取视图缩放比例

在执行缩放操作前或后，通常需要查询当前的缩放比例，以便进行比例相关的计算或显示。

### 获取缩放比例方法

| 方法 | 功能描述 | 返回值 |
|-----|---------|--------|
| `painter.getImgScale()` | 获取当前视图的缩放比例 | float |

Sources: [PySSView.py](PySSView.py#L1032-L1033), [PySSView.py](PySSView.py#L1156-L1157)

### 使用示例

```python
from sunvpy import SSProcess

# 获取当前地图
current_map = SSProcess.getCurrentMap()

# 获取当前缩放比例
# current_scale = painter.getImgScale()
# print(f"当前缩放比例: {current_scale}")

# 根据缩放比例调整其他参数
# if current_scale > 1.0:
#     print("当前为放大视图")
# elif current_scale < 1.0:
#     print("当前为缩小视图")
# else:
#     print("当前为1:1视图")
```

Sources: [PySSView.py](PySSView.py#L1032-L1033), [PySSView.py](PySSView.py#L1156-L1157)

## 视图刷新控制

视图刷新是确保缩放、平移等操作能够立即显示的关键步骤。

### 视图刷新方法

| 方法 | 功能描述 | 参数说明 |
|-----|---------|---------|
| `painter.repaint(erase)` | 立即重绘当前窗口 | `erase`：是否擦除背景，默认为 false |

Sources: [PySSView.py](PySSView.py#L900-L910)

### 使用示例

```python
from sunvpy import SSProcess

# 获取当前地图
current_map = SSProcess.getCurrentMap()

# 执行缩放或平移操作后，刷新视图
# painter.zoomWindow(rect, 1.0)
# painter.repaint(erase=False)  # 不擦除背景，优化性能

# 或者完全重绘
# painter.repaint(erase=True)   # 擦除背景，完全重绘
```

Sources: [PySSView.py](PySSView.py#L900-L910)

## 获取地图矩形信息

地图矩形定义了地图的地理范围，对于计算缩放区域和视图边界非常有用。

### 地图矩形方法

| 方法 | 功能描述 | 参数说明 |
|-----|---------|---------|
| `map.getMapRect(rect, arg3)` | 获取地图矩形范围 | `rect`：输出参数，返回矩形<br>`arg3`：对象类型参数 |

Sources: [PySSMap.py](PySSMap.py#L2428-L2430)

### 使用示例

```python
from sunvpy import SSProcess
from sunvpy.PySSMath import DblRect
from sunvpy.PySSCore import e_Null_Obj

# 获取当前地图
current_map = SSProcess.getCurrentMap()

# 创建矩形对象
rect = DblRect()

# 获取地图矩形范围
# current_map.getMapRect(rect, e_Null_Obj)

# 输出矩形范围
# print(f"矩形范围: ({rect.left()}, {rect.bottom()}) - ({rect.right()}, {rect.top()})")
# print(f"矩形宽度: {rect.width()}")
# print(f"矩形高度: {rect.height()}")
```

Sources: [PySSMap.py](PySSMap.py#L2428-L2430), [ssprocess_mixins/project_mixin.py](ssprocess_mixins/project_mixin.py#L93-L101)

## 对象缩放操作

除了视图缩放外，SunvStation 还支持对地图中的地理对象进行缩放变换。

### 对象缩放方法

| 方法 | 功能描述 | 参数说明 |
|-----|---------|---------|
| `map.zoom(GeoList, spCenter, xScale, yScale, fBaselineAngle)` | 缩放指定的地理对象列表 | `GeoList`：对象列表<br>`spCenter`：缩放中心点<br>`xScale`：X方向缩放因子<br>`yScale`：Y方向缩放因子<br>`fBaselineAngle`：基准线角度 |

Sources: [PySSMap.py](PySSMap.py#L1588-L1590)

### 对象缩放流程

```mermaid
flowchart LR
    A[开始缩放对象] --> B[创建撤销标记]
    B --> C[pushUndoMark]
    C --> D[准备对象列表]
    D --> E[GeoBaseList]
    E --> F[设置缩放参数]
    F --> G[缩放中心点<br>xScale/yScale<br>基准线角度]
    G --> H[调用 map.zoom]
    H --> I{缩放是否成功}
    I -->|成功| J[对象已缩放]
    I -->|失败| K[检查参数有效性]
    J --> L[刷新视图]
    K --> M[重新尝试]
    L --> N[完成]
    
    style A fill:#e1f5ff
    style I fill:#fff4e1
    style L fill:#ffe8e1
```

### 使用示例

```python
from sunvpy import SSProcess
from sunvpy.PySSMath import Point3D, GeoBaseList

# 获取当前地图
current_map = SSProcess.getCurrentMap()

# 创建撤销标记
SSProcess.pushUndoMark("缩放对象")

# 准备要缩放的对象列表
geo_list = GeoBaseList()

# 假设已选择了一些对象，添加到列表中
# geo_list.push_back(selected_geo1)
# geo_list.push_back(selected_geo2)

# 设置缩放中心点
center_point = Point3D(500000.0, 3000000.0, 0.0)

# 设置缩放因子（放大2倍）
x_scale = 2.0
y_scale = 2.0
baseline_angle = 0.0  # 基准线角度（弧度）

# 执行缩放
# success = current_map.zoom(geo_list, center_point, x_scale, y_scale, baseline_angle)
# if success:
#     print("对象缩放成功")
#     # 刷新视图
#     painter.repaint()
# else:
#     print("对象缩放失败")
```

Sources: [PySSMap.py](PySSMap.py#L1588-L1590), [ssprocess_mixins/project_mixin.py](ssprocess_mixins/project_mixin.py#L55-L62)

## 综合应用示例

以下是一个完整的示例，展示了如何综合运用各种缩放控制方法实现自动定位和视图调整功能。

### 示例：自动定位到指定区域并调整比例尺

```python
from sunvpy import SSProcess
from sunvpy.PySSMath import Point3D, DblRect, GeoBaseList
from sunvpy.PySSCore import e_Null_Obj

def zoom_to_region(center_x, center_y, scale_denom):
    """
    缩放到指定区域并设置比例尺
    
    参数:
        center_x: 目标中心点 X 坐标（米）
        center_y: 目标中心点 Y 坐标（米）
        scale_denom: 目标比例尺分母（例如 500 表示 1:500）
    """
    # 获取当前地图
    current_map = SSProcess.getCurrentMap()
    if current_map is None:
        print("错误：当前没有打开的地图")
        return False
    
    # 创建撤销标记
    SSProcess.pushUndoMark("缩放到指定区域")
    
    try:
        # 方法1：直接设置比例尺
        print(f"设置比例尺为 1:{scale_denom}")
        success = current_map.setMapScale(scale_denom)
        if not success:
            print(f"警告：设置比例尺 1:{scale_denom} 失败")
        
        # 方法2：获取当前视图对象（实际实现需根据具体情况）
        # 假设通过某种方式获取了 painter
        # painter = get_current_painter()
        
        # 计算目标矩形范围（基于中心点和比例尺）
        # 这里需要根据实际比例尺和视图尺寸计算矩形范围
        # rect = calculate_rect_from_center_and_scale(center_x, center_y, scale_denom)
        
        # 缩放到目标区域
        # painter.zoomWindow(rect, 1.0)
        
        # 刷新视图
        # painter.repaint(erase=False)
        
        print("视图缩放操作完成")
        return True
        
    except Exception as e:
        print(f"缩放操作失败：{str(e)}")
        return False


# 使用示例
if __name__ == "__main__":
    # 缩放到中心点 (500000, 3000000)，比例尺 1:1000
    success = zoom_to_region(500000.0, 3000000.0, 1000)
    if success:
        print("操作成功")
    else:
        print("操作失败")
```

Sources: [PySSMap.py](PySSMap.py#L2330-L2335), [PySSMap.py](PySSMap.py#L2430-L2432), [ssprocess_mixins/project_mixin.py](ssprocess_mixins/project_mixin.py#L45-L50), [ssprocess_mixins/project_mixin.py](ssprocess_mixins/project_mixin.py#L55-L62)

## 常见问题与解决方案

### 问题1：比例尺设置失败

**症状**：调用 `setMapScale()` 方法返回 false，比例尺未更新。

**可能原因**：
- 传入的比例尺值不在有效范围内
- 当前地图不支持指定的比例尺

**解决方案**：

```python
def safe_set_map_scale(target_scale):
    """安全地设置地图比例尺"""
    current_map = SSProcess.getCurrentMap()
    
    # 检查地图是否存在
    if current_map is None:
        print("错误：当前没有打开的地图")
        return False
    
    # 获取当前比例尺作为参考
    current_scale = current_map.getMapScale()
    print(f"当前比例尺: 1:{current_scale}")
    print(f"目标比例尺: 1:{target_scale}")
    
    # 尝试设置新比例尺
    success = current_map.setMapScale(target_scale)
    
    if success:
        # 验证设置结果
        new_scale = current_map.getMapScale()
        print(f"比例尺已更新为: 1:{new_scale}")
        return True
    else:
        print(f"警告：无法设置比例尺 1:{target_scale}，保持当前比例尺")
        return False
```

Sources: [PySSMap.py](PySSMap.py#L2330-L2335), [PySSMap.py](PySSMap.py#L2430-L2432)

### 问题2：坐标转换结果不准确

**症状**：`geoToScreen()` 或 `screenToGeo()` 返回的坐标值与预期不符。

**可能原因**：
- 地图比例尺或投影设置不正确
- 视图尚未完成渲染

**解决方案**：

```python
def accurate_coordinate_conversion(painter, geo_point):
    """确保准确的坐标转换"""
    # 确保视图是最新的
    painter.repaint(erase=False)
    
    # 获取当前缩放比例
    img_scale = painter.getImgScale()
    print(f"当前缩放比例: {img_scale}")
    
    # 执行坐标转换
    screen_point = painter.geoToScreen(geo_point)
    print(f"地理坐标 ({geo_point.x()}, {geo_point.y()})")
    print(f"屏幕坐标 ({screen_point.x()}, {screen_point.y()})")
    
    # 反向验证
    verified_geo = painter.screenToGeo(screen_point)
    print(f"验证地理坐标 ({verified_geo.x()}, {verified_geo.y()})")
    
    # 检查精度
    error_x = abs(geo_point.x() - verified_geo.x())
    error_y = abs(geo_point.y() - verified_geo.y())
    print(f"转换误差: X={error_x:.6f}, Y={error_y:.6f}")
    
    return screen_point
```

Sources: [PySSView.py](PySSView.py#L1032-L1041), [PySSView.py](PySSView.py#L900-L910)

### 问题3：视图刷新后显示异常

**症状**：调用 `repaint()` 后视图显示不正确或闪烁。

**解决方案**：

```python
def smart_view_update(painter, erase_background=True):
    """智能视图更新"""
    if erase_background:
        # 完全重绘，确保视图完全更新
        painter.repaint(erase=True)
        print("执行完全重绘")
    else:
        # 增量更新，优化性能
        painter.repaint(erase=False)
        print("执行增量更新")
```

Sources: [PySSView.py](PySSView.py#L900-L910)

## 最佳实践

### 1. 批量操作前创建撤销标记

```python
# 在执行多个缩放或变换操作前，创建一个撤销标记
SSProcess.pushUndoMark("批量视图调整")

# 执行一系列操作
# map.zoom(...)
# painter.moveScreenByPoint(...)
# painter.zoomWindow(...)

# 这样所有操作可以作为一个整体被撤销
```

Sources: [ssprocess_mixins/project_mixin.py](ssprocess_mixins/project_mixin.py#L55-L62)

### 2. 缩放操作后刷新视图

```python
# 执行缩放或平移操作
# painter.zoomWindow(rect, z)

# 总是调用刷新以确保视图更新
painter.repaint(erase=False)
```

Sources: [PySSView.py](PySSView.py#L900-L910)

### 3. 使用坐标转换实现精确定位

```python
# 计算目标屏幕位置
target_screen_x = 400
target_screen_y = 300

# 转换为地理坐标
target_geo = painter.screenToGeo(Point2D(target_screen_x, target_screen_y))

# 移动视图到目标位置
# painter.moveScreenByPoint(target_geo, False)
```

Sources: [PySSView.py](PySSView.py#L1034-L1041), [PySSView.py](PySSView.py#L1031-L1033)

### 4. 检查地图有效性

```python
# 在执行任何地图操作前，检查地图是否存在
current_map = SSProcess.getCurrentMap()
if current_map is None:
    print("错误：当前没有打开的地图")
    return False

# 检查地图是否有效
if current_map.getMapScale() <= 0:
    print("错误：地图比例尺无效")
    return False
```

Sources: [ssprocess_mixins/project_mixin.py](ssprocess_mixins/project_mixin.py#L45-L50), [PySSMap.py](PySSMap.py#L2330-L2335)

## 相关页面

掌握了地图缩放控制后，您可以进一步学习以下相关主题：

- **[获取工作空间信息](34-huo-qu-gong-zuo-kong-jian-xin-xi)**：了解如何获取当前工作空间和地图实例
- **[工作空间与地图概念](8-gong-zuo-kong-jian-yu-di-tu-gai-nian)**：深入理解 SunvStation 的工作空间和地图架构
- **[撤销标记管理](25-che-xiao-biao-ji-guan-li)**：学习如何管理撤销/重做操作