Skill: haohaizi0620/dmap-jsapi-skill (version 1.0.0)

--- name: dmap-jsapi-skill description: DMap GL地图 JSAPI (dmapgl) 开发指南。在编写、审查或调试使用DMap地图 API的代码时应运用此技能。适用于涉及地图初始化、覆盖物渲染、图层管理、事件处理、控件交互等任务。当用户提及 dmap、dmapgl、jsapi-gl或相关地图开发需求时自动触发 license: MIT version: 1.0.0 --- # DMap GL JSAPI开发指南 本指南包含地图初始化、标记、弹窗、控件、图层、数据源等核心模块的 API 说明和代码示例,旨在帮助开发者快速集成DMap GL地图并遵循正确的使用方式。 ## 目的 利用此技能,在前端项目中以生产级安全的默认设置实现DMap GL JSAPI功能。 ## 何时使用 在以下场景中参考这些指南: - 创建新的地图页面或组件 - 在地图上添加标记(Marker)、弹窗(Popup)等UI元素 - 配置地图视图(缩放、中心点、旋转、俯仰) - 管理图层和数据源(GeoJSON、矢量瓦片、栅格瓦片等) - 处理地图交互事件(点击、拖拽、缩放等) - 添加地图控件(导航、比例尺、全屏等) - 调试地图渲染或性能问题 ## 快速开始 ### 1. 引入 DMap GL JS 通过 CDN 安装: ```html <!-- CDN --> <link rel="stylesheet" href="http://172.26.64.84/dmapapi/bj2000/v1.0/dmap-gl.css"> <script type="text/javascript" src="http://172.26.64.84/dmapapi/bj2000/v1.0/dmap-gl.js"></script> ``` ### 2. 指定后台服务地址 ```javascript // 注意：此处需改为你的后台服务地址 dmapgl.serviceUrl = 'http://172.26.64.84/api'; ``` ### 3. 初始化地图 ```javascript var map = new dmapgl.Map({ container: 'map', // HTML容器ID style: dmapgl.serviceUrl + '/map/vector/standard/styles/style.json', // 地图样式URL center: [800000, 600000], // 初始中心点 [x, y] 地方平面坐标 zoom: 11, // 初始缩放级别 (7-19) pitch: 0, // 俯仰角(0-85) bearing: 0, // 旋转角度 minZoom: 7, // 最小缩放级别 maxZoom: 19, // 最大缩放级别 maxBounds: [ [716638.2414098255, 548483.5939021005], // 西南角 [894455.0756895209, 728066.4667000007] // 东北角 ], }); // 等待地图加载完成 map.on('load', () => { console.log('地图加载完成'); }); ``` ## 场景示例 ### 地图控制 - **生命周期**: `references/map-init.md` - 掌握 Map 实例创建、配置及 destroy 销毁流程 - **视图交互**: `references/view-control.md` - 控制 zoom(缩放)、center(平移)、pitch(俯仰)、bearing(旋转) > **注意**: DMap 使用地方平面坐标系，中心点为 [800000, 600000]，地图范围为 [716638.24, 548483.59, 894455.08, 728066.47]，缩放级别范围为 7-19。 ### 标记与弹窗 - **标记**: `references/marker.md` - 使用 Marker 标注位置,支持自定义HTML元素 - **弹窗**: `references/popup.md` - 通过 Popup 展示详细信息 ### 图层与数据源 - **数据源**: `references/sources.md` - GeoJSON、矢量瓦片、栅格瓦片、图片、Canvas等数据源 - **图层管理**: `references/layers.md` - 添加、移除、排序图层,动态更新样式 ### 控件与交互 - **控件**: `references/controls.md` - 缩放和旋转按钮、缩放按钮、比例尺、全屏按钮 - **事件系统**: `references/events.md` - 响应点击、拖拽、缩放、移动等交互事件 ### 高级功能 - **动画**: `references/animation.md` - 相机飞行动画、平滑过渡 - **3D地形**: `references/terrain.md` - 启用3D地形渲染 ## 最佳实践 1. **资源释放**: 组件卸载时务必调用 `map.remove()` 或 `map.destroy()`,防止 WebGL 上下文内存泄漏 2. **按需加载**: 仅在需要时添加图层和控件,避免不必要的性能开销 3. **事件监听清理**: 移除组件时取消事件监听,使用 `map.off()` 清理 4. **样式优化**: 使用 `setLayoutProperty` 和 `setPaintProperty` 动态更新样式,而非重新添加图层 5. **数据更新**: GeoJSON 数据频繁更新时使用 `setData()` 而非删除重建 6. **性能监控**: 使用 `map.queryRenderedFeatures()` 进行高效的要素查询 ## API Reference ### [Map](references/api/map.md) Map - 地图核心对象，包含所有地图操作方法、交互处理器(handler)属性、相机控制、样式管理、查询等功能。 **主要内容包括:** - Camera (相机控制): getCenter, setZoom, flyTo, easeTo, fitBounds 等 - Map constraints (地图约束): getMaxBounds, setMaxBounds, setMinZoom, setMaxZoom 等 - Handlers (交互处理器): boxZoom, scrollZoom, dragPan, dragRotate, keyboard, doubleClickZoom, touchZoomRotate 等 - Markers and Controls (标记和控件): addControl, removeControl - Styling (样式): addLayer, addSource, setPaintProperty, addImage, setLight, setTerrain, setFog 等 - Querying features (查询要素): queryRenderedFeatures, querySourceFeatures, queryTerrainElevation - I/O (输入输出): getContainer, getCanvas, getCanvasContainer - Other (其他): loaded, remove, destroy, resize, update, triggerRepaint 等 ### [Properties and Options](references/api/properties.md) 全局属性和工具函数，包括 serviceUrl、version、supported、clearData、setRTLTextPlugin、AnimationOptions、CameraOptions、CustomLayerInterface 等。 ### [Markers and Controls](references/api/markers-controls.md) 用户界面元素，可以在运行时添加到地图上。 **包括:** - IControl - 控件接口规范 - Marker - 标记，支持自定义HTML元素、拖拽、弹窗关联 - Popup - 弹窗，支持HTML内容、动态定位 - NavigationControl - 导航控件(缩放、旋转、俯仰) - GeolocateControl - 地理定位控件 - ScaleControl - 比例尺控件 - FullscreenControl - 全屏控件 - AttributionControl - 版权控件 ### [User Interaction Handlers](references/api/handlers.md) 控制地图的用户交互行为。 **包括:** - BoxZoomHandler - 框选缩放 (Shift+拖拽) - ScrollZoomHandler - 滚轮缩放 - DragPanHandler - 拖拽平移 - DragRotateHandler - 拖拽旋转 (右键拖拽) - KeyboardHandler - 键盘交互 - DoubleClickZoomHandler - 双击缩放 - TouchZoomRotateHandler - 触摸缩放旋转 - TwoFingersTouchPitchHandler - 双指倾斜 - TwoFingersTouchZoomRotateHandler - 双指缩放旋转 每个handler都提供 enable(), disable(), isEnabled() 方法。 ### [Sources](references/api/sources.md) 数据源定义地图上要显示的地理数据。 **包括:** - GeoJSONSource - GeoJSON格式数据源，支持聚类 - VectorTileSource - 矢量瓦片数据源 - RasterTileSource - 栅格瓦片数据源 - RasterArrayTileSource - 栅格数组瓦片数据源 - ImageSource - 单张图片数据源 - VideoSource - 视频数据源 - CanvasSource - Canvas数据源 - ModelSource - 3D模型数据源 ### [Events and Event Types](references/api/events.md) 事件系统和事件类型。 **Evented基类:** - on(type, handler) - 绑定事件 - off(type, handler) - 解绑事件 - once(type, handler) - 一次性事件 - fire(type, data) - 触发事件 **事件对象类型:** - MapMouseEvent - 鼠标事件对象 - MapTouchEvent - 触摸事件对象 - MapWheelEvent - 滚轮事件对象 - MapBoxZoomEvent - 框选缩放事件对象 - MapDataEvent - 数据加载事件对象 **地图事件分类:** - 鼠标事件: click, dblclick, mousedown, mouseup, mousemove, mouseover, mouseout, contextmenu - 触摸事件: touchstart, touchend, touchmove, touchcancel - 视图事件: movestart, move, moveend, zoomstart, zoom, zoomend, rotatestart, rotate, rotateend, pitchstart, pitch, pitchend - 加载事件: load, render, idle, error - 数据事件: dataloading, data, sourcedataloading, sourcedata, styledataloading, styledata - 图层事件: layer.add, layer.remove - 框选缩放事件: boxzoomstart, boxzoomend, boxzoomcancel ### [Geography and Geometry](references/api/geography.md) 用于处理地理坐标、边界框和屏幕位置的类。 **包括:** - LngLat - 地理坐标(经度,纬度) - LngLatBounds - 地理边界框 - Point - 二维点(x, y) - MercatorCoordinate - 墨卡托坐标(3D投影) - Pixel - 像素坐标类型 - PointLike - Point或数字数组的灵活类型 - LngLatLike - LngLat或数组的灵活类型 - LngLatBoundsLike - LngLatBounds或数组的灵活类型 ## Style Specification ### [Layers](references/style/layers.md) fill / line / symbol / circle / heatmap / fill-extrusion / raster / hillshade / background ### [Sources](references/style/sources.md) vector / raster / raster-dem / geojson / image / video / canvas / model ### [Expressions](references/style/expressions.md) 数学运算 / 条件判断 / 字符串操作 / 颜色转换 / 数据驱动样式 ### [Types](references/style/types.md) Color / PromoteId / Formatted / ResolvedImage / VariableAnchorOffsetCollection ### [Root](references/style/root.md) version / name / metadata / center / zoom / bearing / pitch / sources / layers ### [Sprite](references/style/sprite.md) Sprite 图标集配置 ### [Glyphs](references/style/glyphs.md) Glyphs 字体配置 ### [Terrain](references/style/terrain.md) Terrain 地形配置 ### [Fog](references/style/fog.md) Fog 雾效配置 ### [Light](references/style/light.md) Light 光照配置 ### [Transition](references/style/transition.md) Transition 过渡动画配置 ### [Other](references/style/other.md) 其他样式属性 ## 使用 skills 必须遵循的铁律 1. **校验生成代码可用性**: 自我验证校验代码生成的可用性,一定是可以正常运行的 2. **命名空间一致性**: 所有 API 调用使用 `dmapgl` 命名空间 ## 如何使用 1. 如果有相近的"场景示例"那么去阅读场景示例,再阅读场景示例中的涉及的类的api文档。再结合描述/场景示例/api 去完成任务。 2. 在最终的完成任务前,检查用的api用法是否符合文档。

path: references/marker.md
content: 

# 标记 (Marker)

在地图上添加点标记,支持自定义HTML内容和样式。

## 基础用法

### 创建简单标记

```javascript
// 基本标记
const marker = new dmapgl.Marker()
  .setLngLat([800000, 600000])
  .addTo(map);

// 带颜色的标记
const redMarker = new dmapgl.Marker({ color: '#FF0000' })
  .setLngLat([810000, 610000])
  .addTo(map);
```

### 自定义HTML标记

```javascript
// 创建自定义DOM元素
const el = document.createElement('div');
el.className = 'custom-marker';
el.innerHTML = '<div class="marker-icon">📍</div>';

const customMarker = new dmapgl.Marker({ element: el })
  .setLngLat([800000, 600000])
  .addTo(map);
```

CSS样式:

```css
.custom-marker {
  width: 40px;
  height: 40px;
  background: #3b82f6;
  border-radius: 50% 50% 50% 0;
  transform: rotate(-45deg);
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 2px 10px rgba(0,0,0,0.3);
}

.marker-icon {
  transform: rotate(45deg);
  font-size: 20px;
}
```

## Marker 配置选项

```javascript
const marker = new dmapgl.Marker({
  // 自定义DOM元素
  element: customElement,
  
  // 颜色 (仅当未提供element时有效): red, blue, green等
  color: '#FF0000',
  
  // 锚点位置
  anchor: 'center', // center, top, bottom, left, right
  
  // 偏移量 [x, y]
  offset: [0, -20],
  
  // 可拖拽
  draggable: true,
  
  // 旋转角度
  rotation: 45,
  
  // 旋转对齐方式
  rotationAlignment: 'map', // map, viewport, auto
  
  // 缩放对齐方式
  scaleAlignment: 'auto', // auto, map, viewport
  
  // 点击提示文本
  clickTolerance: 3
});
```

## 标记操作

### 获取和设置位置

```javascript
// 获取位置
const lngLat = marker.getLngLat();
console.log('经度:', lngLat.lng);
console.log('纬度:', lngLat.lat);

// 设置位置
marker.setLngLat([810000, 610000]);

// 移动到新位置(带动画)
marker.setLngLat([810000, 610000]);
```

### 拖拽功能

```javascript
// 启用拖拽
marker.setDraggable(true);

// 禁用拖拽
marker.setDraggable(false);

// 监听拖拽事件
marker.on('dragstart', () => {
  console.log('开始拖拽');
});

marker.on('drag', () => {
  const pos = marker.getLngLat();
  console.log('拖拽中:', pos);
});

marker.on('dragend', () => {
  const pos = marker.getLngLat();
  console.log('拖拽结束:', pos);
});
```

### 显示和隐藏

```javascript
// 从地图移除
marker.remove();

// 重新添加到地图
marker.addTo(map);

// 检查是否在地图上
if (marker.getMap()) {
  console.log('标记已添加到地图');
}
```

### Popup 关联

```javascript
// 创建弹窗
const popup = new dmapgl.Popup({ offset: 25 })
  .setHTML('<h3>北京</h3><p>中国首都</p>');

// 将弹窗绑定到标记
const marker = new dmapgl.Marker()
  .setLngLat([800000, 600000])
  .setPopup(popup)
  .addTo(map);

// 切换弹窗显示
marker.togglePopup();

// 打开弹窗
marker.getPopup().addTo(map);

// 关闭弹窗
marker.getPopup().remove();

// 更新弹窗内容
marker.getPopup().setHTML('<h3>新标题</h3><p>新内容</p>');
```

## 多个标记管理

### 批量添加标记

```javascript
const locations = [
  { name: '北京', coords: [800000, 600000] },
  { name: '上海', coords: [850000, 580000] },
  { name: '广州', coords: [780000, 550000] }
];

const markers = [];

locations.forEach(location => {
  const marker = new dmapgl.Marker({ color: '#3b82f6' })
    .setLngLat(location.coords)
    .setPopup(new dmapgl.Popup().setHTML(`<h3>${location.name}</h3>`))
    .addTo(map);
  
  markers.push(marker);
});
```

### 清除所有标记

```javascript
markers.forEach(marker => marker.remove());
markers.length = 0;
```

### 标记集群 (MarkerCluster)

对于大量标记点,使用聚类提高性能:

```javascript
// 需要引入 supercluster 库
import Supercluster from 'supercluster';

const cluster = new Supercluster({
  radius: 40,
  maxZoom: 16
});

// 加载数据
cluster.load(points.map(point => ({
  type: 'Feature',
  geometry: {
    type: 'Point',
    coordinates: [point.lng, point.lat]
  },
  properties: point
})));

// 根据视口获取聚类结果
const clusters = cluster.getClusters(
  map.getBounds().toArray(),
  Math.floor(map.getZoom())
);

// 渲染聚类标记
clusters.forEach(cluster => {
  const marker = new dmapgl.Marker({
    element: createClusterElement(cluster.properties.point_count)
  })
    .setLngLat(cluster.geometry.coordinates)
    .addTo(map);
});
```

## 动画效果

### 脉冲动画标记

```javascript
// HTML
const pulseEl = document.createElement('div');
pulseEl.className = 'pulse-marker';

const marker = new dmapgl.Marker({ element: pulseEl })
  .setLngLat([800000, 600000])
  .addTo(map);
```

CSS:

```css
.pulse-marker {
  width: 20px;
  height: 20px;
  background: #3b82f6;
  border-radius: 50%;
  position: relative;
}

.pulse-marker::before {
  content: '';
  position: absolute;
  width: 100%;
  height: 100%;
  background: inherit;
  border-radius: 50%;
  animation: pulse 2s ease-out infinite;
}

@keyframes pulse {
  0% {
    transform: scale(1);
    opacity: 1;
  }
  100% {
    transform: scale(3);
    opacity: 0;
  }
}
```

### 弹跳动画

```javascript
const bounceEl = document.createElement('div');
bounceEl.className = 'bounce-marker';

function animateBounce() {
  bounceEl.style.transform = 'translateY(0)';
  setTimeout(() => {
    bounceEl.style.transform = 'translateY(-10px)';
  }, 500);
}

setInterval(animateBounce, 1000);
```

## 事件处理

```javascript
const marker = new dmapgl.Marker()
  .setLngLat([800000, 600000])
  .addTo(map);

// 点击事件
marker.getElement().addEventListener('click', (e) => {
  e.stopPropagation(); // 阻止地图点击
  console.log('标记被点击');
});

// 鼠标悬停
marker.getElement().addEventListener('mouseenter', () => {
  marker.getElement().style.cursor = 'pointer';
});

// 右键菜单
marker.getElement().addEventListener('contextmenu', (e) => {
  e.preventDefault();
  showContextMenu(e.clientX, e.clientY);
});
```

## 高级用法

### 自定义图标标记

```javascript
function createIconMarker(iconUrl) {
  const el = document.createElement('div');
  el.className = 'icon-marker';
  el.style.backgroundImage = `url(${iconUrl})`;
  el.style.width = '40px';
  el.style.height = '40px';
  el.style.backgroundSize = 'cover';
  
  return el;
}

const iconMarker = new dmapgl.Marker({ element: createIconMarker('/icons/pin.png') })
  .setLngLat([800000, 600000])
  .addTo(map);
```

### 轨迹回放

```javascript
const path = [
  [800000, 600000],
  [805000, 605000],
  [810000, 610000],
  [815000, 615000]
];

let currentIndex = 0;
const movingMarker = new dmapgl.Marker()
  .setLngLat(path[0])
  .addTo(map);

function moveAlongPath() {
  if (currentIndex < path.length - 1) {
    currentIndex++;
    movingMarker.setLngLat(path[currentIndex]);
    setTimeout(moveAlongPath, 1000);
  }
}

moveAlongPath();
```

### 过滤可见标记

```javascript
// 只显示当前视口内的标记
function updateVisibleMarkers() {
  const bounds = map.getBounds();
  
  allMarkers.forEach(marker => {
    const lngLat = marker.getLngLat();
    if (bounds.contains(lngLat)) {
      marker.addTo(map);
    } else {
      marker.remove();
    }
  });
}

map.on('moveend', updateVisibleMarkers);
```

## 注意事项

1. **性能**: 超过100个标记建议使用 SymbolLayer 或聚类
2. **内存**: 移除标记时调用 `remove()` 释放资源
3. **Z-index**: 标记默认在最上层,可通过CSS调整
4. **响应式**: 标记大小不会随缩放自动调整,需手动处理


path: references/animation.md
content: 

# 动画 (Animation)

DMap GL提供流畅的相机动画和过渡效果。

## 飞行动画 (flyTo)

从一个视图平滑飞行到另一个视图。

### 基础用法

```javascript
map.flyTo({
  center: [810000, 610000],
  zoom: 14,
  duration: 3000  // 毫秒
});
```

### 完整配置

```javascript
map.flyTo({
  // 目标中心点
  center: [810000, 610000],
  
  // 目标缩放级别
  zoom: 15,
  
  // 目标旋转角度
  bearing: 45,
  
  // 目标俯仰角
  pitch: 60,
  
  // 动画时长(毫秒)
  duration: 4000,
  
  // 飞行曲线(越大弧度越大)
  curve: 1.5,
  
  // 飞行速度
  speed: 1.2,
  
  // 屏幕像素/毫秒的最大滚动速度
  screenSpeed: 1,
  
  // 即使减少运动也执行(无障碍)
  essential: true,
  
  // 缓动函数
  easing: (t) => t * t
});
```

### 监听动画状态

```javascript
// 动画开始
map.on('movestart', (e) => {
  if (e.originalEvent && e.originalEvent.flyTo) {
    console.log('飞行动画开始');
  }
});

// 动画结束
map.on('moveend', () => {
  console.log('飞行动画结束');
});

// 中断动画
map.stop();
```

## 平滑过渡 (easeTo)

使用线性插值平滑过渡到新视图。

### 基础用法

```javascript
map.easeTo({
  center: [810000, 610000],
  zoom: 14,
  duration: 2000
});
```

### 自定义缓动

```javascript
// 缓入缓出
map.easeTo({
  center: [810000, 610000],
  duration: 2000,
  easing: (t) => {
    return t < 0.5 
      ? 2 * t * t 
      : -1 + (4 - 2 * t) * t;
  }
});

// 弹性效果
map.easeTo({
  zoom: 15,
  duration: 1500,
  easing: (t) => {
    return Math.pow(2, -10 * t) * Math.sin((t - 0.075) * (2 * Math.PI) / 0.3) + 1;
  }
});
```

### 仅改变单个属性

```javascript
// 仅缩放
map.easeTo({ zoom: 12 });

// 仅旋转
map.easeTo({ bearing: 90 });

// 仅俯仰
map.easeTo({ pitch: 45 });

// 仅平移
map.easeTo({ center: [116.5, 39.9] });
```

## 立即跳转 (jumpTo)

无动画立即跳转到新视图。

```javascript
map.jumpTo({
  center: [810000, 610000],
  zoom: 14,
  bearing: 45,
  pitch: 60
});
```

## 专用动画方法

### zoomTo - 缩放到指定级别

```javascript
map.zoomTo(15, {
  duration: 1000,
  easing: (t) => t
});
```

### panTo - 平移到指定位置

```javascript
map.panTo([810000, 610000], {
  duration: 1500,
  easing: (t) => t
});
```

### rotateTo - 旋转到指定角度

```javascript
map.rotateTo(90, {
  duration: 1000
});

// 重置为北向
map.resetNorth({ duration: 500 });
```

### pitchTo - 俯仰到指定角度

```javascript
map.pitchTo(60, {
  duration: 1000
});
```

## 相机动画序列

### 链式动画

```javascript
function cameraTour() {
  map.flyTo({
    center: [800000, 600000],
    zoom: 12,
    duration: 3000
  });
  
  setTimeout(() => {
    map.flyTo({
      center: [116.407, 39.904],
      zoom: 15,
      bearing: 45,
      duration: 3000
    });
  }, 3000);
  
  setTimeout(() => {
    map.flyTo({
      center: [800000, 600000],
      zoom: 12,
      bearing: 0,
      duration: 3000
    });
  }, 6000);
}

cameraTour();
```

### Promise封装

```javascript
function flyToPromise(options) {
  return new Promise((resolve) => {
    map.flyTo({
      ...options,
      essential: true
    });
    
    map.once('moveend', resolve);
  });
}

async function cameraSequence() {
  await flyToPromise({
    center: [800000, 600000],
    zoom: 12,
    duration: 2000
  });
  
  await flyToPromise({
    center: [116.407, 39.904],
    zoom: 15,
    bearing: 45,
    duration: 2000
  });
  
  await flyToPromise({
    center: [800000, 600000],
    zoom: 12,
    duration: 2000
  });
  
  console.log('动画序列完成');
}

cameraSequence();
```

## 标记动画

### 移动标记

```javascript
const marker = new dmapgl.Marker()
  .setLngLat([800000, 600000])
  .addTo(map);

// 平滑移动标记
function moveMarker(targetLngLat, duration = 1000) {
  const startLngLat = marker.getLngLat();
  const startTime = performance.now();
  
  function animate(currentTime) {
    const elapsed = currentTime - startTime;
    const progress = Math.min(elapsed / duration, 1);
    
    // 线性插值
    const lng = startLngLat.lng + (targetLngLat[0] - startLngLat.lng) * progress;
    const lat = startLngLat.lat + (targetLngLat[1] - startLngLat.lat) * progress;
    
    marker.setLngLat([lng, lat]);
    
    if (progress < 1) {
      requestAnimationFrame(animate);
    }
  }
  
  requestAnimationFrame(animate);
}

moveMarker([116.407, 39.904], 2000);
```

### 脉冲动画

```javascript
const pulseEl = document.createElement('div');
pulseEl.className = 'pulse-marker';

const marker = new dmapgl.Marker({ element: pulseEl })
  .setLngLat([800000, 600000])
  .addTo(map);
```

CSS:

```css
.pulse-marker {
  width: 20px;
  height: 20px;
  background: #3b82f6;
  border-radius: 50%;
  position: relative;
}

.pulse-marker::before,
.pulse-marker::after {
  content: '';
  position: absolute;
  width: 100%;
  height: 100%;
  background: inherit;
  border-radius: 50%;
  animation: pulse 2s ease-out infinite;
}

.pulse-marker::after {
  animation-delay: 1s;
}

@keyframes pulse {
  0% {
    transform: scale(1);
    opacity: 1;
  }
  100% {
    transform: scale(3);
    opacity: 0;
  }
}
```

### 弹跳动画

```javascript
const bounceEl = document.createElement('div');
bounceEl.className = 'bounce-marker';

const marker = new dmapgl.Marker({ element: bounceEl })
  .setLngLat([800000, 600000])
  .addTo(map);
```

CSS:

```css
.bounce-marker {
  width: 30px;
  height: 30px;
  background: #ef4444;
  border-radius: 50% 50% 50% 0;
  transform: rotate(-45deg);
  animation: bounce 1s ease infinite;
}

@keyframes bounce {
  0%, 100% {
    transform: rotate(-45deg) translateY(0);
  }
  50% {
    transform: rotate(-45deg) translateY(-10px);
  }
}
```

## 图层动画

### 淡入淡出

```javascript
// 添加图层时设置初始透明度
map.addLayer({
  id: 'fade-layer',
  type: 'fill',
  source: 'my-source',
  paint: {
    'fill-opacity': 0
  }
});

// 淡入
function fadeIn(layerId, duration = 1000) {
  const start = performance.now();
  const targetOpacity = 1;
  
  function animate(currentTime) {
    const elapsed = currentTime - start;
    const progress = Math.min(elapsed / duration, 1);
    
    map.setPaintProperty(layerId, 'fill-opacity', progress * targetOpacity);
    
    if (progress < 1) {
      requestAnimationFrame(animate);
    }
  }
  
  requestAnimationFrame(animate);
}

fadeIn('fade-layer', 1500);

// 淡出
function fadeOut(layerId, duration = 1000) {
  const start = performance.now();
  const startOpacity = map.getPaintProperty(layerId, 'fill-opacity');
  
  function animate(currentTime) {
    const elapsed = currentTime - start;
    const progress = Math.min(elapsed / duration, 1);
    
    map.setPaintProperty(
      layerId, 
      'fill-opacity', 
      startOpacity * (1 - progress)
    );
    
    if (progress < 1) {
      requestAnimationFrame(animate);
    }
  }
  
  requestAnimationFrame(animate);
}
```

### 渐变显示路径

```javascript
// 添加线图层
map.addLayer({
  id: 'animated-line',
  type: 'line',
  source: 'path-source',
  paint: {
    'line-color': '#3b82f6',
    'line-width': 4,
    'line-dasharray': [0, 1000]
  }
});

// 动画显示
function drawLine(duration = 2000) {
  const start = performance.now();
  
  function animate(currentTime) {
    const elapsed = currentTime - start;
    const progress = Math.min(elapsed / duration, 1);
    
    map.setPaintProperty('animated-line', 'line-dasharray', [
      progress * 1000,
      1000
    ]);
    
    if (progress < 1) {
      requestAnimationFrame(animate);
    }
  }
  
  requestAnimationFrame(animate);
}

drawLine(3000);
```

## 视口动画

### 适配边界动画

```javascript
map.fitBounds([
  [750000, 560000],
  [850000, 650000]
], {
  padding: 50,
  duration: 2000,
  easing: (t) => t
});
```

### 3D建筑动画

```javascript
// 动态改变建筑高度
let buildingHeight = 0;
const targetHeight = 100;

function animateBuildings() {
  buildingHeight += 2;
  
  if (buildingHeight <= targetHeight) {
    map.setPaintProperty('3d-buildings', 'fill-extrusion-height', buildingHeight);
    requestAnimationFrame(animateBuildings);
  }
}

animateBuildings();
```

## 实用示例

### 轨迹回放

```javascript
class TrackPlayer {
  constructor(trackData) {
    this.track = trackData;
    this.currentIndex = 0;
    this.isPlaying = false;
    this.speed = 1000; // 每点间隔(ms)
    
    this.marker = new dmapgl.Marker()
      .setLngLat(trackData[0])
      .addTo(map);
    
    // 添加轨迹线
    map.addSource('track', {
      type: 'geojson',
      data: {
        type: 'Feature',
        geometry: {
          type: 'LineString',
          coordinates: trackData.slice(0, 1)
        }
      }
    });
    
    map.addLayer({
      id: 'track-line',
      type: 'line',
      source: 'track',
      paint: {
        'line-color': '#3b82f6',
        'line-width': 3
      }
    });
  }
  
  play() {
    if (this.isPlaying) return;
    this.isPlaying = true;
    this.animate();
  }
  
  pause() {
    this.isPlaying = false;
  }
  
  animate() {
    if (!this.isPlaying || this.currentIndex >= this.track.length - 1) {
      this.isPlaying = false;
      return;
    }
    
    this.currentIndex++;
    
    // 更新标记位置
    const pos = this.track[this.currentIndex];
    this.marker.setLngLat(pos);
    
    // 更新轨迹线
    const source = map.getSource('track');
    source.setData({
      type: 'Feature',
      geometry: {
        type: 'LineString',
        coordinates: this.track.slice(0, this.currentIndex + 1)
      }
    });
    
    // 相机跟随
    map.panTo(pos, { duration: this.speed });
    
    setTimeout(() => this.animate(), this.speed);
  }
  
  reset() {
    this.pause();
    this.currentIndex = 0;
    this.marker.setLngLat(this.track[0]);
    
    map.getSource('track').setData({
      type: 'Feature',
      geometry: {
        type: 'LineString',
        coordinates: [this.track[0]]
      }
    });
  }
}

// 使用
const player = new TrackPlayer([
  [800000, 600000],
  [805000, 605000],
  [810000, 610000],
  [815000, 615000]
]);

player.play();
```

### 闪烁效果

```javascript
function blinkLayer(layerId, times = 3, interval = 500) {
  let count = 0;
  
  function toggle() {
    if (count >= times * 2) return;
    
    const visibility = count % 2 === 0 ? 'none' : 'visible';
    map.setLayoutProperty(layerId, 'visibility', visibility);
    
    count++;
    setTimeout(toggle, interval);
  }
  
  toggle();
}

blinkLayer('highlight-layer', 5, 300);
```

## 注意事项

1. **性能**: 避免同时运行过多动画
2. **内存**: 动画完成后清理资源
3. **用户体验**: 尊重用户的"减少运动"偏好
4. **中断处理**: 新动画会中断旧动画
5. **缓动函数**: 选择合适的缓动提升体验
6. **时长控制**: 动画不宜过长或过短
7. **可访问性**: 提供关闭动画的选项


path: references/sources.md
content: 

# 数据源 (Sources)

数据源定义地图上显示的数据,包括GeoJSON、矢量瓦片、栅格瓦片等类型。

## GeoJSONSource

用于显示GeoJSON格式的数据。

### 基础用法

```javascript
// 从URL加载
map.addSource('points', {
  type: 'geojson',
  data: 'https://example.com/data.geojson'
});

// 直接传入数据
map.addSource('points', {
  type: 'geojson',
  data: {
    type: 'FeatureCollection',
    features: [
      {
        type: 'Feature',
        geometry: {
          type: 'Point',
          coordinates: [800000, 600000]
        },
        properties: {
          name: '北京'
        }
      }
    ]
  }
});
```

### 配置选项

```javascript
map.addSource('data', {
  type: 'geojson',
  data: geojsonData,
  
  // 最大缩放级别
  maxzoom: 14,
  
  // 瓦片缓冲大小
  buffer: 128,
  
  // 容差(简化几何)
  tolerance: 0.375,
  
  // 启用聚类
  cluster: true,
  clusterMaxZoom: 14,
  clusterRadius: 50,
  
  // 聚类属性
  clusterProperties: {
    sum: ['+', ['get', 'value']]
  },
  
  // 行缓冲
  lineMetrics: false,
  
  // 生成ID
  generateId: false,
  
  // 促进ID
  promoteId: null
});
```

### 动态更新数据

```javascript
// 获取数据源
const source = map.getSource('points');

// 更新数据
source.setData(newGeoJsonData);

// 增量更新
source.updateData({
  type: 'FeatureCollection',
  features: updatedFeatures
});
```

### 聚类功能

```javascript
map.addSource('earthquakes', {
  type: 'geojson',
  data: earthquakeData,
  cluster: true,
  clusterMaxZoom: 14,
  clusterRadius: 50
});

// 添加聚类图层
map.addLayer({
  id: 'clusters',
  type: 'circle',
  source: 'earthquakes',
  filter: ['has', 'point_count'],
  paint: {
    'circle-color': [
      'step',
      ['get', 'point_count'],
      '#51bbd6',
      100,
      '#f1f075',
      750,
      '#f28cb1'
    ],
    'circle-radius': [
      'step',
      ['get', 'point_count'],
      20,
      100,
      30,
      750,
      40
    ]
  }
});

// 点击聚类展开
map.on('click', 'clusters', (e) => {
  const features = map.queryRenderedFeatures(e.point, {
    layers: ['clusters']
  });
  const clusterId = features[0].properties.cluster_id;
  
  const source = map.getSource('earthquakes');
  source.getClusterExpansionZoom(clusterId, (err, zoom) => {
    if (err) return;
    
    map.easeTo({
      center: features[0].geometry.coordinates,
      zoom: zoom
    });
  });
});
```

## VectorTileSource

矢量瓦片数据源。

### 基础用法

```javascript

// 自定义瓦片服务器
map.addSource('custom-vector', {
  type: 'vector',
  tiles: [
    'https://example.com/tiles/{z}/{x}/{y}.pbf'
  ],
  minzoom: 7,
  maxzoom: 14
});
```

### 配置选项

```javascript
map.addSource('vector-data', {
  type: 'vector',
  
  // 瓦片URL模板
  tiles: ['https://example.com/{z}/{x}/{y}.pbf'],
  
  // 或使用TileJSON URL
  url: 'https://example.com/tilejson.json',
  
  // 最小/最大缩放
  minzoom: 7,
  maxzoom: 14,
  
  // 瓦片尺寸
  tileSize: 512,
  
  // 方案(tms或xyz)
  scheme: 'xyz',
  
  // 边界
  bounds: [-180, -85.051129, 180, 85.051129],
  
  //  attribution
  attribution: '© Data Contributors'
});
```

### 使用矢量图层

```javascript
// 添加矢量数据源
map.addSource('buildings', {
  type: 'vector',
  url: 'https://example.com/tiles/{z}/{x}/{y}.pbf'
});

// 引用矢量图层
map.addLayer({
  id: '3d-buildings',
  source: 'buildings',
  'source-layer': 'building',
  type: 'fill-extrusion',
  minzoom: 15,
  paint: {
    'fill-extrusion-color': '#aaa',
    'fill-extrusion-height': ['get', 'height'],
    'fill-extrusion-opacity': 0.6
  }
});
```

## RasterTileSource

栅格瓦片数据源(图片瓦片)。

### 基础用法

```javascript

// 自定义瓦片服务器
map.addSource('custom-raster', {
  type: 'raster',
  tiles: [
    'https://example.com/tiles/{z}/{x}/{y}.png'
  ],
  tileSize: 512
});
```

### WMS服务

```javascript
map.addSource('wms-layer', {
  type: 'raster',
  tiles: [
    'https://img.nj.gov/imagerywms/Natural2015?' +
    'bbox={bbox-epsg-3857}' +
    '&format=image/png' +
    '&service=WMS' +
    '&version=1.1.1' +
    '&request=GetMap' +
    '&srs=EPSG:3857' +
    '&transparent=true' +
    '&width=256' +
    '&height=256' +
    '&layers=Natural2015'
  ],
  tileSize: 256
});
```

### 配置选项

```javascript
map.addSource('raster-data', {
  type: 'raster',
  tiles: ['https://example.com/{z}/{x}/{y}.png'],
  
  // 瓦片尺寸
  tileSize: 512,
  
  // 最小/最大缩放
  minzoom: 7,
  maxzoom: 19,
  
  // 边界
  bounds: [-180, -85.051129, 180, 85.051129],
  
  // 方案
  scheme: 'xyz',
  
  //  attribution
  attribution: '© Tile Provider'
});
```

## ImageSource

单张图片数据源。

### 基础用法

```javascript
map.addSource('image-source', {
  type: 'image',
  url: 'https://example.com/map-overlay.png',
  coordinates: [
    [-76.54, 39.18],  // 左上
    [-76.52, 39.18],  // 右上
    [-76.52, 39.17],  // 右下
    [-76.54, 39.17]   // 左下
  ]
});

// 添加图层
map.addLayer({
  id: 'image-layer',
  type: 'raster',
  source: 'image-source',
  paint: {
    'raster-opacity': 0.85
  }
});
```

### 动态更新

```javascript
const source = map.getSource('image-source');

// 更新坐标
source.setCoordinates([
  [-76.543, 39.185],
  [-76.528, 39.183],
  [-76.529, 39.176],
  [-76.545, 39.178]
]);

// 更新图片和坐标
source.updateImage({
  url: 'https://example.com/new-image.png',
  coordinates: [
    [-76.543, 39.185],
    [-76.528, 39.183],
    [-76.529, 39.176],
    [-76.545, 39.178]
  ]
});
```

## VideoSource

视频数据源。

### 基础用法

```javascript
map.addSource('video-source', {
  type: 'video',
  urls: [
    'https://example.com/video.mp4',
    'https://example.com/video.webm'
  ],
  coordinates: [
    [-76.54, 39.18],
    [-76.52, 39.18],
    [-76.52, 39.17],
    [-76.54, 39.17]
  ]
});

map.addLayer({
  id: 'video-layer',
  type: 'raster',
  source: 'video-source'
});
```

### 控制播放

```javascript
const videoSource = map.getSource('video-source');

// 获取视频元素
const video = videoSource.getVideo();

// 播放
video.play();

// 暂停
video.pause();

// 监听事件
video.addEventListener('ended', () => {
  console.log('视频播放结束');
});
```

## CanvasSource

Canvas数据源。

### 基础用法

```javascript
map.addSource('canvas-source', {
  type: 'canvas',
  canvas: 'my-canvas-id', // Canvas元素ID
  animate: true,
  coordinates: [
    [-76.54, 39.18],
    [-76.52, 39.18],
    [-76.52, 39.17],
    [-76.54, 39.17]
  ]
});

map.addLayer({
  id: 'canvas-layer',
  type: 'raster',
  source: 'canvas-source'
});
```

### 动态绘制

```javascript
const canvas = document.getElementById('my-canvas');
const ctx = canvas.getContext('2d');

function drawOnCanvas() {
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  
  // 绘制内容
  ctx.fillStyle = 'rgba(255, 0, 0, 0.5)';
  ctx.fillRect(50, 50, 100, 100);
  
  requestAnimationFrame(drawOnCanvas);
}

drawOnCanvas();

// 控制动画
const source = map.getSource('canvas-source');
source.play();   // 开始动画
source.pause();  // 暂停动画
```

## 数据源管理

### 检查数据源是否存在

```javascript
if (map.getSource('my-source')) {
  console.log('数据源存在');
}
```

### 移除数据源

```javascript
// 先移除依赖该数据源的图层
if (map.getLayer('my-layer')) {
  map.removeLayer('my-layer');
}

// 再移除数据源
map.removeSource('my-source');
```

### 获取数据源信息

```javascript
const source = map.getSource('my-source');

// 获取元数据
source.on('dataloading', () => {
  console.log('正在加载数据');
});

source.on('data', (e) => {
  console.log('数据已加载', e);
});

source.on('error', (e) => {
  console.error('数据加载错误', e.error);
});
```

## 实用示例

### 实时轨迹追踪

```javascript
// 初始化GeoJSON
const trackGeoJson = {
  type: 'FeatureCollection',
  features: [{
    type: 'Feature',
    geometry: {
      type: 'LineString',
      coordinates: []
    }
  }]
};

map.addSource('track', {
  type: 'geojson',
  data: trackGeoJson
});

map.addLayer({
  id: 'track-line',
  type: 'line',
  source: 'track',
  paint: {
    'line-color': '#3b82f6',
    'line-width': 3
  }
});

// 实时更新
function updateTrack(position) {
  const source = map.getSource('track');
  const coords = source.getData().features[0].geometry.coordinates;
  
  coords.push([position.lng, position.lat]);
  
  // 保持最近100个点
  if (coords.length > 100) {
    coords.shift();
  }
  
  source.setData({
    type: 'FeatureCollection',
    features: [{
      type: 'Feature',
      geometry: {
        type: 'LineString',
        coordinates: coords
      }
    }]
  });
}
```

### 热力图数据源

```javascript
map.addSource('heatmap-data', {
  type: 'geojson',
  data: pointData
});

map.addLayer({
  id: 'heatmap',
  type: 'heatmap',
  source: 'heatmap-data',
  paint: {
    'heatmap-weight': ['interpolate', ['linear'], ['get', 'magnitude'], 0, 0, 6, 1],
    'heatmap-intensity': ['interpolate', ['linear'], ['zoom'], 0, 1, 9, 3],
    'heatmap-color': [
      'interpolate',
      ['linear'],
      ['heatmap-density'],
      0, 'rgba(0, 0, 255, 0)',
      0.2, 'royalblue',
      0.4, 'cyan',
      0.6, 'lime',
      0.8, 'yellow',
      1, 'red'
    ],
    'heatmap-radius': ['interpolate', ['linear'], ['zoom'], 0, 2, 9, 20],
    'heatmap-opacity': 0.8
  }
});
```

## 注意事项

1. **性能**: 大数据集使用矢量瓦片而非GeoJSON
2. **内存**: 及时移除不需要的数据源
3. **CORS**: 确保外部资源支持跨域访问
4. **缓存**: 瓦片数据会自动缓存,可通过maxTileCacheSize控制
5. **更新频率**: 频繁更新时使用setData而非删除重建
6. **聚类**: 大量点数据时启用聚类提高性能


path: references/layers.md
content: 

# 图层管理 (Layers)

图层定义数据如何在地图上渲染,包括样式、颜色、大小等视觉属性。

## 添加图层

### 基础用法

```javascript
map.addLayer({
  id: 'my-layer',
  type: 'circle',
  source: 'my-source',
  paint: {
    'circle-radius': 5,
    'circle-color': '#3b82f6'
  }
});
```

### 完整配置

```javascript
map.addLayer({
  // 图层ID(必须唯一)
  id: 'buildings',
  
  // 图层类型
  type: 'fill',
  
  // 数据源
  source: 'building-source',
  
  // 矢量瓦片的图层名(仅矢量瓦片需要)
  'source-layer': 'building',
  
  // 最小缩放级别
  minzoom: 12,
  
  // 最大缩放级别
  maxzoom: 19,
  
  // 过滤器
  filter: ['==', 'type', 'residential'],
  
  // 布局属性
  layout: {
    visibility: 'visible' // visible 或 none
  },
  
  // 绘制属性
  paint: {
    'fill-color': '#cccccc',
    'fill-opacity': 0.7
  },
  
  // 元数据
  metadata: {
    description: '建筑物图层'
  }
});
```

## 图层类型

### Circle - 圆点

```javascript
map.addLayer({
  id: 'points',
  type: 'circle',
  source: 'data',
  paint: {
    'circle-radius': 8,
    'circle-color': '#3b82f6',
    'circle-stroke-width': 2,
    'circle-stroke-color': '#ffffff',
    'circle-opacity': 0.8,
    'circle-blur': 0
  }
});
```

### Symbol - 符号/文本

```javascript
// 图标
map.addLayer({
  id: 'icons',
  type: 'symbol',
  source: 'places',
  layout: {
    'icon-image': 'restaurant-15',
    'icon-size': 1,
    'icon-allow-overlap': false,
    'icon-ignore-placement': false
  }
});

// 文本标签
map.addLayer({
  id: 'labels',
  type: 'symbol',
  source: 'places',
  layout: {
    'text-field': ['get', 'name'],
    'text-font': ['Open Sans Regular'],
    'text-size': 14,
    'text-offset': [0, -1.5],
    'text-anchor': 'top',
    'text-allow-overlap': false
  },
  paint: {
    'text-color': '#333333',
    'text-halo-color': '#ffffff',
    'text-halo-width': 2
  }
});
```

### Line - 线

```javascript
map.addLayer({
  id: 'roads',
  type: 'line',
  source: 'streets',
  'source-layer': 'road',
  layout: {
    'line-cap': 'round',
    'line-join': 'round'
  },
  paint: {
    'line-color': '#3b82f6',
    'line-width': 3,
    'line-opacity': 0.8,
    'line-dasharray': [2, 2] // 虚线
  }
});
```

### Fill - 填充面

```javascript
map.addLayer({
  id: 'parks',
  type: 'fill',
  source: 'landuse',
  'source-layer': 'park',
  paint: {
    'fill-color': '#4ade80',
    'fill-opacity': 0.5,
    'fill-outline-color': '#22c55e'
  }
});
```

### Fill Extrusion - 3D挤出

```javascript
map.addLayer({
  id: '3d-buildings',
  type: 'fill-extrusion',
  source: 'buildings',
  'source-layer': 'building',
  minzoom: 15,
  paint: {
    'fill-extrusion-color': '#aaa',
    'fill-extrusion-height': ['get', 'height'],
    'fill-extrusion-base': ['get', 'min_height'],
    'fill-extrusion-opacity': 0.6
  }
});
```

### Raster - 栅格

```javascript
map.addLayer({
  id: 'satellite',
  type: 'raster',
  source: 'satellite-source',
  paint: {
    'raster-opacity': 0.8,
    'raster-brightness-min': 0,
    'raster-brightness-max': 1,
    'raster-saturation': 0,
    'raster-contrast': 0
  }
});
```

### Heatmap - 热力图

```javascript
map.addLayer({
  id: 'heatmap',
  type: 'heatmap',
  source: 'earthquakes',
  paint: {
    'heatmap-weight': [
      'interpolate',
      ['linear'],
      ['get', 'magnitude'],
      0, 0,
      6, 1
    ],
    'heatmap-intensity': ['interpolate', ['linear'], ['zoom'], 0, 1, 9, 3],
    'heatmap-color': [
      'interpolate',
      ['linear'],
      ['heatmap-density'],
      0, 'rgba(0, 0, 255, 0)',
      0.2, 'royalblue',
      0.4, 'cyan',
      0.6, 'lime',
      0.8, 'yellow',
      1, 'red'
    ],
    'heatmap-radius': ['interpolate', ['linear'], ['zoom'], 0, 2, 9, 20],
    'heatmap-opacity': 0.8
  }
});
```

### Background - 背景

```javascript
map.addLayer({
  id: 'background',
  type: 'background',
  paint: {
    'background-color': '#f0f0f0',
    'background-opacity': 1
  }
});
```

## 图层操作

### 检查图层是否存在

```javascript
if (map.getLayer('my-layer')) {
  console.log('图层存在');
}
```

### 移除图层

```javascript
map.removeLayer('my-layer');
```

### 移动图层顺序

```javascript
// 移动到最上层
map.moveLayer('my-layer');

// 移动到指定图层之下
map.moveLayer('my-layer', 'below-layer-id');

// 移动到指定索引
const layers = map.getStyle().layers;
map.moveLayer('my-layer', null, 5);
```

### 显示/隐藏图层

```javascript
// 隐藏
map.setLayoutProperty('my-layer', 'visibility', 'none');

// 显示
map.setLayoutProperty('my-layer', 'visibility', 'visible');

// 切换
function toggleLayer(layerId) {
  const visibility = map.getLayoutProperty(layerId, 'visibility');
  map.setLayoutProperty(
    layerId,
    'visibility',
    visibility === 'visible' ? 'none' : 'visible'
  );
}
```

## 动态更新样式

### 更新绘制属性

```javascript
// 更新单个属性
map.setPaintProperty('my-layer', 'circle-radius', 10);
map.setPaintProperty('my-layer', 'circle-color', '#ff0000');

// 获取当前属性值
const radius = map.getPaintProperty('my-layer', 'circle-radius');
console.log('当前半径:', radius);
```

### 更新布局属性

```javascript
map.setLayoutProperty('my-layer', 'text-size', 16);
map.setLayoutProperty('my-layer', 'icon-size', 1.5);
```

### 更新过滤器

```javascript
// 只显示特定类型
map.setFilter('my-layer', ['==', 'type', 'city']);

// 多条件过滤
map.setFilter('my-layer', [
  'all',
  ['==', 'type', 'city'],
  ['>', 'population', 1000000]
]);

// 清除过滤
map.setFilter('my-layer', null);
```

## 数据驱动样式

### 基于属性设置样式

```javascript
map.addLayer({
  id: 'cities',
  type: 'circle',
  source: 'places',
  paint: {
    // 根据人口设置半径
    'circle-radius': [
      'interpolate',
      ['linear'],
      ['get', 'population'],
      0, 5,
      1000000, 10,
      10000000, 20
    ],
    
    // 根据类型设置颜色
    'circle-color': [
      'match',
      ['get', 'type'],
      'city', '#3b82f6',
      'town', '#10b981',
      'village', '#f59e0b',
      '#999999' // 默认值
    ]
  }
});
```

### 缩放级别驱动样式

```javascript
map.addLayer({
  id: 'roads',
  type: 'line',
  source: 'streets',
  paint: {
    // 根据缩放级别调整线宽
    'line-width': [
      'interpolate',
      ['exponential', 1.5],
      ['zoom'],
      5, 1,
      10, 2,
      15, 5,
      20, 10
    ]
  }
});
```

### 表达式示例

```javascript
// 数学运算
'circle-radius': ['+', ['get', 'base_radius'], 2]

// 条件判断
'circle-color': [
  'case',
  ['>', ['get', 'value'], 100], '#ff0000',
  ['>', ['get', 'value'], 50], '#ffff00',
  '#00ff00'
]

// 字符串操作
'text-field': ['upcase', ['get', 'name']]

// 数组操作
'circle-color': ['at', 0, ['get', 'colors']]

// 类型转换
'circle-radius': ['to-number', ['get', 'radius'], 5]
```

## 图层查询

### 查询渲染的要素

```javascript
// 点击位置查询
map.on('click', (e) => {
  const features = map.queryRenderedFeatures(e.point, {
    layers: ['my-layer']
  });
  
  if (features.length > 0) {
    console.log('点击的要素:', features[0].properties);
  }
});

// 矩形区域查询
const features = map.queryRenderedFeatures([
  [x1, y1], // 左上角
  [x2, y2]  // 右下角
], {
  layers: ['layer1', 'layer2']
});
```

### 查询源数据

```javascript
const features = map.querySourceFeatures('my-source', {
  sourceLayer: 'my-source-layer',
  filter: ['==', 'type', 'city']
});

console.log(`找到 ${features.length} 个要素`);
```

## 图层交互

### 点击事件

```javascript
map.on('click', 'my-layer', (e) => {
  const feature = e.features[0];
  
  new dmapgl.Popup()
    .setLngLat(e.lngLat)
    .setHTML(`<h3>${feature.properties.name}</h3>`)
    .addTo(map);
});
```

### 鼠标悬停

```javascript
// 悬停高亮
let hoveredStateId = null;

map.on('mousemove', 'my-layer', (e) => {
  if (e.features.length > 0) {
    if (hoveredStateId !== null) {
      map.setFeatureState(
        { source: 'my-source', id: hoveredStateId },
        { hover: false }
      );
    }
    
    hoveredStateId = e.features[0].id;
    map.setFeatureState(
      { source: 'my-source', id: hoveredStateId },
      { hover: true }
    );
  }
});

map.on('mouseleave', 'my-layer', () => {
  if (hoveredStateId !== null) {
    map.setFeatureState(
      { source: 'my-source', id: hoveredStateId },
      { hover: false }
    );
  }
  hoveredStateId = null;
});

// 使用状态驱动样式
map.addLayer({
  id: 'my-layer',
  type: 'fill',
  source: 'my-source',
  paint: {
    'fill-color': [
      'case',
      ['boolean', ['feature-state', 'hover'], false],
      '#ff0000',
      '#3b82f6'
    ]
  }
});
```

### 光标变化

```javascript
map.on('mouseenter', 'my-layer', () => {
  map.getCanvas().style.cursor = 'pointer';
});

map.on('mouseleave', 'my-layer', () => {
  map.getCanvas().style.cursor = '';
});
```

## 实用示例

### 图层切换器

```javascript
const layers = [
  { id: 'satellite', name: '卫星图' },
  { id: 'streets', name: '街道图' },
  { id: 'dark', name: '深色图' }
];

layers.forEach(layer => {
  const button = document.createElement('button');
  button.textContent = layer.name;
  button.onclick = () => {
    // 隐藏所有图层
    layers.forEach(l => {
      map.setLayoutProperty(l.id, 'visibility', 'none');
    });
    
    // 显示选中图层
    map.setLayoutProperty(layer.id, 'visibility', 'visible');
  };
  
  document.getElementById('layer-switcher').appendChild(button);
});
```

### 动态过滤

```javascript
// 搜索框过滤
document.getElementById('search').addEventListener('input', (e) => {
  const query = e.target.value.toLowerCase();
  
  if (query === '') {
    map.setFilter('cities', null);
  } else {
    map.setFilter('cities', [
      '>=',
      ['length', ['downcase', ['get', 'name']]],
      0
    ]);
  }
});
```

### 图层透明度控制

```javascript
document.getElementById('opacity-slider').addEventListener('input', (e) => {
  const opacity = parseFloat(e.target.value);
  map.setPaintProperty('my-layer', 'fill-opacity', opacity);
});
```

## 注意事项

1. **图层顺序**: 后添加的图层在上层
2. **性能**: 避免过多图层,合并相似样式
3. **内存**: 移除不需要的图层
4. **ID唯一性**: 每个图层ID必须唯一
5. **数据源依赖**: 移除数据源前先移除相关图层
6. **表达式性能**: 复杂表达式影响渲染性能
7. **可见性**: 隐藏图层仍会占用资源,考虑移除


path: references/view-control.md
content: 

# 视图控制

> **坐标系说明**: DMap 使用地方平面坐标系，中心点为 `[800000, 600000]`，地图范围为 `[716638.24, 548483.59, 894455.08, 728066.47]`，缩放级别范围为 `7-19`。

控制地图的相机视图,包括缩放、平移、旋转和俯仰。

## 缩放 (Zoom)

### 获取当前缩放级别

```javascript
const zoom = map.getZoom();
console.log('当前缩放:', zoom);
```

### 设置缩放级别

```javascript
// 立即设置
map.setZoom(15);

// 动画过渡
map.easeTo({ zoom: 15, duration: 2000 });
map.flyTo({ zoom: 15, duration: 3000 });

// 缩放到特定级别
map.zoomTo(12, { duration: 1000 });
```

### 缩放限制

```javascript
// 初始化时设置
var map = new dmapgl.Map({
  minZoom: 7,
  maxZoom: 18
});

// 动态修改
map.setMinZoom(3);
map.setMaxZoom(20);
```

## 中心点 (Center)

### 获取当前中心点

```javascript
const center = map.getCenter();
console.log('经度:', center.lng);
console.log('纬度:', center.lat);
```

### 设置中心点

```javascript
// 立即设置 [lng, lat]
map.setCenter([810000, 610000]);

// 动画过渡
map.easeTo({ 
  center: [810000, 610000],
  duration: 2000 
});

map.flyTo({ 
  center: [810000, 610000],
  duration: 3000,
  zoom: 14
});

// 平移到新位置
map.panTo([810000, 610000], { duration: 1000 });
```

## 旋转 (Bearing)

地图逆时针旋转角度(单位:度)。

### 获取当前旋转角度

```javascript
const bearing = map.getBearing();
console.log('旋转角度:', bearing);
```

### 设置旋转角度

```javascript
// 立即设置
map.setBearing(45);

// 动画过渡
map.easeTo({ bearing: 90, duration: 2000 });
map.rotateTo(180, { duration: 1000 });

// 重置为北向
map.resetNorth();
```

## 俯仰 (Pitch)

地图倾斜角度(0-85度)。

### 获取当前俯仰角

```javascript
const pitch = map.getPitch();
console.log('俯仰角:', pitch);
```

### 设置俯仰角

```javascript
// 立即设置
map.setPitch(60);

// 动画过渡
map.easeTo({ pitch: 45, duration: 2000 });

// 俯仰到特定角度
map.pitchTo(30, { duration: 1000 });
```

## 边界 (Bounds)

### 获取当前可视区域

```javascript
const bounds = map.getBounds();
console.log('西南角:', bounds.getSouthWest());
console.log('东北角:', bounds.getNorthEast());
```

### 适配到边界

```javascript
// 适配到指定边界
map.fitBounds([
  [750000, 560000], // 西南角 [x, y]
  [850000, 650000]  // 东北角 [x, y]
], {
  padding: 50,    // 内边距(像素)
  duration: 2000, // 动画时长
  maxZoom: 19     // 最大缩放
});

// 适配单个点
map.fitBounds([[800000, 600000]], {
  padding: 100,
  zoom: 14
});
```

### 设置边界约束

```javascript
// 初始化时设置
var map = new dmapgl.Map({
  maxBounds: [
    [716638.2414098255, 548483.5939021005], // 西南角
    [894455.0756895209, 728066.4667000007]  // 东北角
  ]
});

// 动态设置
map.setMaxBounds([
  [716638.2414098255, 548483.5939021005],
  [894455.0756895209, 728066.4667000007]
]);

// 移除约束
map.setMaxBounds(null);
```

## 综合动画

### easeTo - 平滑过渡

```javascript
map.easeTo({
  center: [810000, 610000],
  zoom: 14,
  bearing: 45,
  pitch: 60,
  duration: 3000,
  easing: (t) => t * t // 自定义缓动函数
});
```

### flyTo - 飞行动画

```javascript
map.flyTo({
  center: [820000, 620000],
  zoom: 15,
  bearing: 90,
  pitch: 45,
  duration: 4000,
  essential: true, // 即使减少运动也执行
  curve: 1.5,      // 飞行曲线
  speed: 1.2       // 飞行速度
});
```

### jumpTo - 立即跳转

```javascript
map.jumpTo({
  center: [800000, 600000],
  zoom: 12,
  bearing: 0,
  pitch: 0
});
```

## 相机控制

### 获取相机状态

```javascript
const camera = map.getCameraOptions();
console.log(camera);
// { center: LngLat, zoom: number, bearing: number, pitch: number }
```

### 禁用/启用交互

```javascript
// 禁用所有交互
map.dragPan.disable();
map.scrollZoom.disable();
map.boxZoom.disable();
map.keyboard.disable();

// 禁用特定方向
map.touchZoomRotate.disableRotation();

// 重新启用
map.dragPan.enable();
```

## 监听视图变化

```javascript
// 移动开始
map.on('movestart', () => {
  console.log('开始移动');
});

// 移动中
map.on('move', () => {
  const center = map.getCenter();
  const zoom = map.getZoom();
  console.log(`当前位置: ${center.lng.toFixed(4)}, ${center.lat.toFixed(4)}, 缩放: ${zoom}`);
});

// 移动结束
map.on('moveend', () => {
  console.log('移动结束');
});

// 缩放变化
map.on('zoomstart', () => {});
map.on('zoom', () => {});
map.on('zoomend', () => {});

// 旋转变化
map.on('rotatestart', () => {});
map.on('rotate', () => {});
map.on('rotateend', () => {});
```

## 实用示例

### 回到初始视图

```javascript
const initialView = {
  center: [800000, 600000],
  zoom: 11,
  bearing: 0,
  pitch: 0
};

function resetView() {
  map.flyTo({
    ...initialView,
    duration: 2000
  });
}
```

### 跟踪用户位置

```javascript
const geolocate = new dmapgl.GeolocateControl({
  positionOptions: { enableHighAccuracy: true },
  trackUserLocation: true,
  showUserHeading: true
});

map.addControl(geolocate);

geolocate.on('geolocate', (e) => {
  const lng = e.coords.longitude;
  const lat = e.coords.latitude;
  
  map.flyTo({
    center: [lng, lat],
    zoom: 16,
    duration: 1500
  });
});
```

### 视口查询要素

```javascript
// 获取当前可视区域内的要素
const features = map.queryRenderedFeatures({
  layers: ['buildings']
});

console.log(`可视区域内有 ${features.length} 个建筑物`);
```

## 注意事项

1. **性能**: 频繁调用 `flyTo` 或 `easeTo` 会影响性能
2. **动画队列**: 新的动画会取消正在进行的动画
3. **边界检查**: 确保坐标在有效范围内
4. **交互冲突**: 程序化控制时注意与用户交互的冲突
5. **内存**: 大量动画时注意清理定时器


path: references/controls.md
content: 

# 地图控件 (Controls)

DMap GL提供多种内置控件,增强地图交互功能。

## NavigationControl - 导航控件

缩放和旋转按钮。

### 基础用法

```javascript
const nav = new dmapgl.NavigationControl();
map.addControl(nav);
```

### 配置选项

```javascript
const nav = new dmapgl.NavigationControl({
  // 显示哪些按钮
  showZoom: true,      // 缩放按钮
  showCompass: true,   // 指南针/重置旋转
  visualizePitch: true // 显示俯仰指示器
});

map.addControl(nav);
```

### 位置控制

```javascript
// 添加到右上角(默认)
map.addControl(nav, 'top-right');

// 其他位置
map.addControl(nav, 'top-left');
map.addControl(nav, 'bottom-right');
map.addControl(nav, 'bottom-left');
```

## ScaleControl - 比例尺

显示当前地图比例。

### 基础用法

```javascript
const scale = new dmapgl.ScaleControl();
map.addControl(scale, 'bottom-left');
```

### 配置选项

```javascript
const scale = new dmapgl.ScaleControl({
  maxWidth: 100,        // 最大宽度(像素)
  unit: 'metric'        // 单位: 'imperial'(英里), 'metric'(米), 'nautical'(海里)
});

map.addControl(scale, 'bottom-left');
```

### 动态更新

```javascript
// 更新单位
scale.setUnit('imperial');
```

## FullscreenControl - 全屏控件

切换地图全屏显示。

### 基础用法

```javascript
const fullscreen = new dmapgl.FullscreenControl();
map.addControl(fullscreen, 'top-right');
```

### 配置容器

```javascript
// 指定全屏容器(默认为地图容器)
const fullscreen = new dmapgl.FullscreenControl({
  container: document.getElementById('fullscreen-container')
});

map.addControl(fullscreen);
```

### 事件监听

```javascript
fullscreen.on('fullscreenchange', () => {
  if (document.fullscreenElement) {
    console.log('进入全屏');
  } else {
    console.log('退出全屏');
  }
});
```

## AttributionControl - 版权信息

显示数据来源版权信息。

### 基础用法

```javascript
const attribution = new dmapgl.AttributionControl({
  compact: false,  // 紧凑模式
  customAttribution: '© 自定义版权信息'
});

map.addControl(attribution);
```

### 配置选项

```javascript
const attribution = new dmapgl.AttributionControl({
  // 紧凑模式(小屏幕自动启用)
  compact: true,
  
  // 自定义版权文本
  customAttribution: [
    '© DMap',
    '© OpenStreetMap contributors'
  ]
});
```

## 自定义控件

### 创建简单控件

```javascript
class CustomControl {
  constructor() {
    this._map = null;
    this._container = null;
  }
  
  onAdd(map) {
    this._map = map;
    
    // 创建容器
    this._container = document.createElement('div');
    this._container.className = 'mapboxgl-ctrl mapboxgl-ctrl-group';
    
    // 创建按钮
    const button = document.createElement('button');
    button.type = 'button';
    button.innerHTML = '🎯';
    button.title = '回到中心点';
    
    button.addEventListener('click', () => {
      map.flyTo({
        center: [800000, 600000],
        zoom: 11
      });
    });
    
    this._container.appendChild(button);
    
    return this._container;
  }
  
  onRemove() {
    this._container.parentNode.removeChild(this._container);
    this._map = undefined;
  }
}

// 使用
const customCtrl = new CustomControl();
map.addControl(customCtrl, 'top-right');
```

### 复杂控件示例

```javascript
class LayerSwitcher {
  constructor(layers) {
    this.layers = layers;
    this._map = null;
    this._container = null;
  }
  
  onAdd(map) {
    this._map = map;
    
    this._container = document.createElement('div');
    this._container.className = 'layer-switcher mapboxgl-ctrl';
    
    // 标题
    const title = document.createElement('h4');
    title.textContent = '图层切换';
    this._container.appendChild(title);
    
    // 图层列表
    this.layers.forEach(layer => {
      const item = document.createElement('div');
      item.className = 'layer-item';
      
      const checkbox = document.createElement('input');
      checkbox.type = 'checkbox';
      checkbox.id = layer.id;
      checkbox.checked = true;
      
      const label = document.createElement('label');
      label.htmlFor = layer.id;
      label.textContent = layer.name;
      
      checkbox.addEventListener('change', (e) => {
        const visibility = e.target.checked ? 'visible' : 'none';
        map.setLayoutProperty(layer.mapLayerId, 'visibility', visibility);
      });
      
      item.appendChild(checkbox);
      item.appendChild(label);
      this._container.appendChild(item);
    });
    
    return this._container;
  }
  
  onRemove() {
    this._container.parentNode.removeChild(this._container);
    this._map = undefined;
  }
}

// 使用
const layerSwitcher = new LayerSwitcher([
  { id: 'roads', name: '道路', mapLayerId: 'road-layer' },
  { id: 'buildings', name: '建筑物', mapLayerId: 'building-layer' },
  { id: 'labels', name: '标签', mapLayerId: 'label-layer' }
]);

map.addControl(layerSwitcher, 'top-right');
```

CSS样式:

```css
.layer-switcher {
  background: white;
  padding: 10px;
  border-radius: 4px;
  box-shadow: 0 2px 10px rgba(0,0,0,0.1);
  min-width: 150px;
}

.layer-switcher h4 {
  margin: 0 0 10px 0;
  font-size: 14px;
}

.layer-item {
  display: flex;
  align-items: center;
  margin: 5px 0;
}

.layer-item input {
  margin-right: 8px;
}

.layer-item label {
  cursor: pointer;
  font-size: 13px;
}
```

## 控件管理

### 移除控件

```javascript
// 移除特定控件
map.removeControl(nav);

// 或者通过引用
const controls = [];
const nav = new dmapgl.NavigationControl();
controls.push(nav);
map.addControl(nav);

// 清理所有控件
controls.forEach(ctrl => map.removeControl(ctrl));
controls.length = 0;
```

### 检查控件是否存在

```javascript
// 没有直接的方法,需要自己跟踪
const hasControl = controls.includes(someControl);
```

## 实用示例

### 组合控件

```javascript
// 创建控件组
function addStandardControls(map) {
  // 导航
  map.addControl(new dmapgl.NavigationControl(), 'top-right');
  
  // 比例尺
  map.addControl(new dmapgl.ScaleControl({ maxWidth: 80 }), 'bottom-left');
  
  // 全屏
  map.addControl(new dmapgl.FullscreenControl(), 'top-right');
  
  // 定位
  map.addControl(new dmapgl.GeolocateControl({
    positionOptions: { enableHighAccuracy: true },
    trackUserLocation: true
  }), 'top-right');
  
  // 版权
  map.addControl(new dmapgl.AttributionControl({
    compact: true
  }));
}

addStandardControls(map);
```

### 响应式控件

```javascript
function setupResponsiveControls(map) {
  const nav = new dmapgl.NavigationControl();
  const scale = new dmapgl.ScaleControl();
  
  function updateControls() {
    const isMobile = window.innerWidth < 768;
    
    // 移动端隐藏部分控件
    if (isMobile) {
      map.removeControl(nav);
      map.removeControl(scale);
    } else {
      map.addControl(nav, 'top-right');
      map.addControl(scale, 'bottom-left');
    }
  }
  
  window.addEventListener('resize', updateControls);
  updateControls();
}
```

### 主题切换

```javascript
class ThemeControl {
  constructor() {
    this._map = null;
    this._container = null;
    this._isDark = false;
  }
  
  onAdd(map) {
    this._map = map;
    
    this._container = document.createElement('div');
    this._container.className = 'mapboxgl-ctrl mapboxgl-ctrl-group theme-control';
    
    const button = document.createElement('button');
    button.type = 'button';
    button.innerHTML = '🌙';
    button.title = '切换主题';
    
    button.addEventListener('click', () => {
      this.toggleTheme();
    });
    
    this._container.appendChild(button);
    return this._container;
  }
  
  toggleTheme() {
    this._isDark = !this._isDark;
    
    const style = this._isDark 
      ? 'zyzx://vector/black/styles/style.json'
      : 'zyzx://vector/shallow/styles/style.json';
    
    this._map.setStyle(style);
    
    // 更新图标
    const button = this._container.querySelector('button');
    button.innerHTML = this._isDark ? '☀️' : '🌙';
  }
  
  onRemove() {
    this._container.parentNode.removeChild(this._container);
    this._map = undefined;
  }
}

map.addControl(new ThemeControl(), 'top-right');
```

## 注意事项

1. **位置选择**: 避免控件重叠,合理规划位置
2. **性能**: 控件数量影响性能,保持简洁
3. **移动端**: 考虑触摸设备的可用性
4. **无障碍**: 确保控件可键盘访问
5. **样式定制**: 通过CSS类名自定义控件外观
6. **内存管理**: 移除控件时调用 `onRemove()`


path: references/terrain.md
content: 

# 地形和特效 (Terrain & Effects)

DMap GL支持3D地形渲染和大气效果。

## Terrain - 3D地形

启用基于DEM数据的3D地形。

### 配置地形

```javascript
// 添加DEM数据源
map.addSource('terrain-dem', {
  type: 'raster-dem',
  tiles: [
      dmapgl.serviceUrl + '/map/img?year=&type=DEM&z={z}&x={x}&y={y}'
  ],
  tileSize: 512,
  maxzoom: 14
});

// 启用地形
map.setTerrain({
  source: 'terrain-dem',
  exaggeration: 1.5  // 地形夸张程度
});
```

### 地形选项

- `source` - DEM数据源ID
- `exaggeration` - 地形夸张倍数(0-2)

### 移除地形

```javascript
map.setTerrain(null);
```

### 示例:山地可视化

```javascript
map.on('load', () => {
  // 添加地形
  map.addSource('dmap-dem', {
    type: 'raster-dem',
    tiles: [
      dmapgl.serviceUrl + '/map/img?year=&type=DEM&z={z}&x={x}&y={y}'
    ],
  });
  
  map.setTerrain({
    source: 'dmap-dem',
    exaggeration: 1.5
  });
  
  // 添加天空雾增强效果
  map.setFog({
    'range': [1, 5],
    'horizon-blend': 0.1,
    'color': 'white',
    'high-color': '#30ccff',
    'space-color': '#ade0fa',
    'star-intensity': 0.0
  });
  
  // 设置3D视角
  map.setPitch(60);
  map.setBearing(-20);
});
```

## Fog - 雾效

创建大气雾效,增强深度感。

### 配置雾效

```javascript
map.setFog({
  range: [0.5, 10],        // 雾的范围 [near, far]
  color: '#ffffff',         // 雾的颜色
  'horizon-blend': 0.1      // 地平线混合
});
```

### 雾效属性

- `range` - 雾的可见范围 [近端, 远端]
- `color` - 雾的颜色
- `horizon-blend` - 地平线混合度(0-1)
- `high-color` - 高空颜色
- `space-color` - 太空颜色
- `star-intensity` - 星星强度

### 动态雾效

```javascript
// 根据时间改变雾效
function updateFogByTime() {
  const hour = new Date().getHours();
  
  if (hour >= 6 && hour < 18) {
    // 白天
    map.setFog({
      range: [0.5, 10],
      color: '#ffffff',
      'horizon-blend': 0.1
    });
  } else {
    // 夜晚
    map.setFog({
      range: [0.5, 15],
      color: '#000033',
      'high-color': '#000066',
      'horizon-blend': 0.2,
      'star-intensity': 0.6
    });
  }
}

updateFogByTime();
```

### 移除雾效

```javascript
map.setFog(null);
```

## Light - 光照

控制3D图层的光照效果。

### 配置光照

```javascript
map.setLight({
  anchor: 'viewport',  // viewport 或 map
  position: [1, 90, 80], // [x, y, z]
  intensity: 0.5,       // 强度
  color: '#ffffff'      // 颜色
});
```

### 光照属性

- `anchor` - 锚点(viewport/map)
- `position` - 光源位置 [x, y, z]
- `intensity` - 光照强度(0-1)
- `color` - 光照颜色

### 动态光照

```javascript
// 模拟日照变化
function updateSunPosition() {
  const time = Date.now() / 1000;
  
  map.setLight({
    anchor: 'map',
    position: [
      Math.cos(time * 0.0001) * 100,
      Math.sin(time * 0.0001) * 100,
      50
    ],
    intensity: 0.8
  });
  
  requestAnimationFrame(updateSunPosition);
}

updateSunPosition();
```


## 实用示例

### 昼夜循环

```javascript
class DayNightCycle {
  constructor(map) {
    this.map = map;
    this.time = 0;
    this.duration = 60000; // 1分钟一个周期
  }
  
  start() {
    this.animate();
  }
  
  animate() {
    this.time += 16; // ~60fps
    const progress = (this.time % this.duration) / this.duration;
    const angle = progress * Math.PI * 2;
    
    // 更新光照
    this.map.setLight({
      anchor: 'map',
      position: [
        Math.cos(angle) * 100,
        Math.sin(angle) * 100,
        50
      ],
      intensity: Math.max(0.2, Math.sin(angle))
    });
    
    // 更新雾效
    if (Math.sin(angle) > 0.3) {
      // 白天
      this.map.setFog({
        range: [0.5, 10],
        color: '#ffffff',
        'horizon-blend': 0.1
      });
    } else {
      // 夜晚
      this.map.setFog({
        range: [0.5, 15],
        color: '#000033',
        'high-color': '#000066',
        'star-intensity': 0.8
      });
    }
    
    requestAnimationFrame(() => this.animate());
  }
}

const cycle = new DayNightCycle(map);
cycle.start();
```


### 3D地形剖面

```javascript
// 启用3D地形
map.addSource('terrain', {
  type: 'raster-dem',
  tiles: [
     dmapgl.serviceUrl + '/map/img?year=&type=DEM&z={z}&x={x}&y={y}'
  ],
});

map.setTerrain({
  source: 'terrain',
  exaggeration: 2
});

// 添加3D建筑
map.addLayer({
  id: '3d-buildings',
  type: 'fill-extrusion',
  source: 'composite',
  'source-layer': 'building',
  minzoom: 15,
  paint: {
    'fill-extrusion-color': '#aaa',
    'fill-extrusion-height': ['get', 'height'],
    'fill-extrusion-opacity': 0.7
  }
});

// 设置观察角度
map.setPitch(75);
map.setBearing(45);
map.setZoom(16);
```

### 星空效果

```javascript
// 夜晚星空
map.setFog({
  range: [1, 10],
  color: '#000011',
  'high-color': '#000033',
  'space-color': '#000000',
  'star-intensity': 1,
  'horizon-blend': 0.1
});

// 添加光照
map.setLight({
  anchor: 'map',
  position: [0, 0, 50],
  intensity: 0.3,
  color: '#6666ff'
});
```

## 注意事项

1. **性能**: 地形和特效会影响性能,移动端谨慎使用
2. **数据源**: 地形需要DEM数据源
3. **兼容性**: 检查浏览器WebGL支持
4. **缩放级别**: 地形在高缩放级别效果更好
5. **组合效果**: 多个特效可以组合使用
6. **内存**: 特效会占用额外显存


path: references/popup.md
content: 

# 弹窗 (Popup)

在地图上显示信息弹窗,可独立使用或与标记关联。

## 基础用法

### 创建基本弹窗

```javascript
const popup = new dmapgl.Popup()
  .setLngLat([800000, 600000])
  .setHTML('<h3>北京</h3><p>中国首都</p>')
  .addTo(map);
```

### 关闭按钮

```javascript
// 带关闭按钮(默认)
const popup = new dmapgl.Popup({ closeButton: true })
  .setLngLat([800000, 600000])
  .setHTML('内容')
  .addTo(map);

// 无关闭按钮
const popup = new dmapgl.Popup({ closeButton: false })
  .setLngLat([800000, 600000])
  .setHTML('内容')
  .addTo(map);
```

## Popup 配置选项

```javascript
const popup = new dmapgl.Popup({
  // 是否显示关闭按钮
  closeButton: true,
  
  // 点击地图其他区域关闭
  closeOnClick: true,
  
  // 按ESC键关闭
  closeOnEscape: true,
  
  // 滚动时关闭
  closeOnMove: false,
  
  // 焦点管理
  focusAfterOpen: true,
  
  // 偏移量(像素或Pixel对象)
  offset: 25,
  // 或针对不同位置设置不同偏移
  offset: {
    'top': [0, 0],
    'bottom': [0, -30],
    'left': [15, 0],
    'right': [-15, 0]
  },
  
  // 最大宽度
  maxWidth: '300px',
  
  // CSS类名
  className: 'custom-popup'
});
```

## 内容设置

### HTML内容

```javascript
popup.setHTML(`
  <div class="popup-content">
    <h3>标题</h3>
    <img src="/images/photo.jpg" alt="照片" />
    <p>详细描述信息...</p>
    <button onclick="handleClick()">操作按钮</button>
  </div>
`);
```

### 文本内容

```javascript
popup.setText('纯文本内容,自动转义HTML');
```

### DOM元素

```javascript
const content = document.createElement('div');
content.className = 'popup-custom';
content.innerHTML = '<h3>自定义内容</h3>';

popup.setDOMContent(content);
```

### 动态更新内容

```javascript
// 更新HTML
popup.setHTML('<h3>新标题</h3><p>新内容</p>');

// 更新文本
popup.setText('新文本');

// 更新DOM
popup.setDOMContent(newElement);
```

## 位置控制

### 设置位置

```javascript
// 设置弹窗位置
popup.setLngLat([810000, 610000]);

// 获取当前位置
const lngLat = popup.getLngLat();
console.log('经度:', lngLat.lng);
console.log('纬度:', lngLat.lat);
```

### 锚点定位

```javascript
// 设置锚点位置
popup.setAnchor('bottom'); // top, bottom, left, right, center

// 自动调整(默认)
popup.setAnchor('auto');
```

## 显示和隐藏

### 添加到地图

```javascript
// 方法1: 链式调用
new dmapgl.Popup()
  .setLngLat([800000, 600000])
  .setHTML('内容')
  .addTo(map);

// 方法2: 单独添加
popup.addTo(map);
```

### 移除弹窗

```javascript
// 从地图移除
popup.remove();

// 检查是否在地图上
if (popup.isOpen()) {
  console.log('弹窗已打开');
}
```

### 切换显示

```javascript
// 切换显示/隐藏
if (popup.isOpen()) {
  popup.remove();
} else {
  popup.addTo(map);
}
```

## 与标记关联

```javascript
// 创建标记和弹窗
const marker = new dmapgl.Marker()
  .setLngLat([800000, 600000]);

const popup = new dmapgl.Popup({ offset: 25 })
  .setHTML('<h3>北京</h3><p>点击查看详情</p>');

// 关联弹窗到标记
marker.setPopup(popup);
marker.addTo(map);

// 点击标记自动显示弹窗
// 或者手动控制
marker.on('click', () => {
  popup.addTo(map);
});
```

## 事件处理

```javascript
const popup = new dmapgl.Popup();

// 打开事件
popup.on('open', () => {
  console.log('弹窗已打开');
});

// 关闭事件
popup.on('close', () => {
  console.log('弹窗已关闭');
});

// 监听地图点击关闭
map.on('click', () => {
  if (popup.isOpen()) {
    popup.remove();
  }
});
```

## 高级用法

### 富媒体弹窗

```javascript
const richPopup = new dmapgl.Popup({ maxWidth: '400px' })
  .setLngLat([800000, 600000])
  .setHTML(`
    <div class="rich-popup">
      <div class="popup-header">
        <h2>故宫博物院</h2>
        <span class="rating">⭐⭐⭐⭐⭐</span>
      </div>
      <div class="popup-image">
        <img src="/images/forbidden-city.jpg" alt="故宫" />
      </div>
      <div class="popup-body">
        <p>中国明清两代的皇家宫殿,世界文化遗产。</p>
        <ul>
          <li>开放时间: 8:30-17:00</li>
          <li>门票: ¥60</li>
          <li>地址: 北京市东城区景山前街4号</li>
        </ul>
      </div>
      <div class="popup-footer">
        <button class="btn-primary">导航</button>
        <button class="btn-secondary">收藏</button>
      </div>
    </div>
  `)
  .addTo(map);
```

CSS样式:

```css
.rich-popup {
  font-family: Arial, sans-serif;
}

.popup-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 10px;
  border-bottom: 1px solid #eee;
}

.popup-image img {
  width: 100%;
  height: 150px;
  object-fit: cover;
}

.popup-body {
  padding: 10px;
}

.popup-footer {
  padding: 10px;
  display: flex;
  gap: 10px;
}

.btn-primary, .btn-secondary {
  flex: 1;
  padding: 8px;
  border: none;
  border-radius: 4px;
  cursor: pointer;
}

.btn-primary {
  background: #3b82f6;
  color: white;
}

.btn-secondary {
  background: #e5e7eb;
  color: #374151;
}
```

### 异步加载内容

```javascript
const loadingPopup = new dmapgl.Popup()
  .setLngLat([800000, 600000])
  .setHTML('<div class="loading">加载中...</div>')
  .addTo(map);

// 异步获取数据
fetch(`/api/poi/${poiId}`)
  .then(res => res.json())
  .then(data => {
    loadingPopup.setHTML(`
      <div class="poi-info">
        <h3>${data.name}</h3>
        <p>${data.description}</p>
      </div>
    `);
  })
  .catch(err => {
    loadingPopup.setHTML('<p class="error">加载失败</p>');
  });
```

### 多个弹窗管理

```javascript
let activePopup = null;

function showPopup(lngLat, content) {
  // 关闭之前的弹窗
  if (activePopup) {
    activePopup.remove();
  }
  
  // 创建新弹窗
  activePopup = new dmapgl.Popup({ offset: 25 })
    .setLngLat(lngLat)
    .setHTML(content)
    .addTo(map);
}

// 使用
map.on('click', (e) => {
  showPopup(e.lngLat, `<p>坐标: ${e.lngLat.lng.toFixed(4)}, ${e.lngLat.lat.toFixed(4)}</p>`);
});
```

### 自定义动画

```javascript
// CSS动画
.custom-popup .dmapgl-popup-content {
  animation: fadeInUp 0.3s ease-out;
}

@keyframes fadeInUp {
  from {
    opacity: 0;
    transform: translateY(10px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}
```

### 响应式弹窗

```javascript
function createResponsivePopup(content) {
  const isMobile = window.innerWidth < 768;
  
  return new dmapgl.Popup({
    maxWidth: isMobile ? '90vw' : '400px',
    offset: isMobile ? [0, -10] : 25,
    className: isMobile ? 'mobile-popup' : 'desktop-popup'
  }).setHTML(content);
}
```

## 实用示例

### 点击地图显示坐标

```javascript
map.on('click', (e) => {
  new dmapgl.Popup()
    .setLngLat(e.lngLat)
    .setHTML(`
      <div>
        <strong>点击位置</strong><br/>
        经度: ${e.lngLat.lng.toFixed(6)}<br/>
        纬度: ${e.lngLat.lat.toFixed(6)}
      </div>
    `)
    .addTo(map);
});
```

### POI信息展示

```javascript
const pois = [
  { id: 1, name: '故宫', lng: 116.397, lat: 39.916, desc: '明清皇宫' },
  { id: 2, name: '天坛', lng: 116.407, lat: 39.882, desc: '祭天场所' }
];

pois.forEach(poi => {
  const marker = new dmapgl.Marker()
    .setLngLat([poi.lng, poi.lat]);
  
  const popup = new dmapgl.Popup({ offset: 25 })
    .setHTML(`
      <div class="poi-popup">
        <h4>${poi.name}</h4>
        <p>${poi.desc}</p>
        <a href="/detail/${poi.id}" target="_blank">查看详情</a>
      </div>
    `);
  
  marker.setPopup(popup).addTo(map);
});
```

### 表单验证提示

```javascript
function showValidationError(lngLat, message) {
  const popup = new dmapgl.Popup({
    closeButton: false,
    closeOnClick: false,
    className: 'error-popup'
  })
    .setLngLat(lngLat)
    .setHTML(`<div class="error">⚠️ ${message}</div>`)
    .addTo(map);
  
  // 3秒后自动关闭
  setTimeout(() => {
    popup.remove();
  }, 3000);
}
```

## 注意事项

1. **性能**: 避免同时打开过多弹窗
2. **内存**: 不再使用时调用 `remove()` 清理
3. **层级**: 弹窗默认在最上层,可通过CSS z-index调整
4. **移动端**: 考虑触摸设备的交互体验
5. **无障碍**: 确保弹窗内容可访问,支持键盘操作
6. **XSS防护**: 用户输入内容使用 `setText()` 而非 `setHTML()`


path: references/events.md
content: 

# 事件系统 (Events)

DMap GL使用事件系统响应地图交互和状态变化。

## 事件绑定

### 基础用法

```javascript
// 绑定事件
map.on('click', (e) => {
  console.log('地图被点击', e);
});

// 绑定到特定图层
map.on('click', 'my-layer', (e) => {
  console.log('图层被点击', e.features[0]);
});

// 解绑事件
map.off('click', handler);

// 只触发一次
map.once('load', () => {
  console.log('地图加载完成');
});
```

## 鼠标事件

### click - 点击

```javascript
map.on('click', (e) => {
  console.log('点击位置:', e.lngLat);
  console.log('像素坐标:', e.point);
  console.log('原始事件:', e.originalEvent);
  
  // 查询点击位置的要素
  const features = map.queryRenderedFeatures(e.point);
  console.log('要素数量:', features.length);
});

// 图层点击
map.on('click', 'buildings', (e) => {
  const building = e.features[0].properties;
  console.log('建筑物信息:', building);
});
```

### dblclick - 双击

```javascript
map.on('dblclick', (e) => {
  console.log('双击位置:', e.lngLat);
});
```

### mousedown / mouseup - 鼠标按下/释放

```javascript
map.on('mousedown', (e) => {
  console.log('鼠标按下');
});

map.on('mouseup', (e) => {
  console.log('鼠标释放');
});
```

### mousemove - 鼠标移动

```javascript
map.on('mousemove', (e) => {
  const coords = `${e.lngLat.lng.toFixed(4)}, ${e.lngLat.lat.toFixed(4)}`;
  document.getElementById('coords').textContent = coords;
});

// 图层悬停
map.on('mousemove', 'cities', (e) => {
  map.getCanvas().style.cursor = 'pointer';
  
  // 高亮显示
  if (e.features.length > 0) {
    showTooltip(e.point, e.features[0].properties.name);
  }
});

map.on('mouseleave', 'cities', () => {
  map.getCanvas().style.cursor = '';
  hideTooltip();
});
```

### mouseover / mouseout - 鼠标进入/离开

```javascript
map.on('mouseover', 'my-layer', (e) => {
  console.log('鼠标进入图层');
});

map.on('mouseout', 'my-layer', (e) => {
  console.log('鼠标离开图层');
});
```

### contextmenu - 右键菜单

```javascript
map.on('contextmenu', (e) => {
  e.preventDefault();
  
  showContextMenu(e.originalEvent.clientX, e.originalEvent.clientY, {
    lng: e.lngLat.lng,
    lat: e.lngLat.lat
  });
});
```

## 触摸事件

### touchstart / touchmove / touchend

```javascript
map.on('touchstart', (e) => {
  console.log('触摸开始', e.points);
});

map.on('touchmove', (e) => {
  console.log('触摸移动');
});

map.on('touchend', (e) => {
  console.log('触摸结束');
});
```

## 地图状态事件

### load - 地图加载完成

```javascript
map.on('load', () => {
  console.log('地图样式加载完成');
  
  // 安全地添加图层和数据源
  addCustomLayers();
});
```

### render - 渲染帧

```javascript
let frameCount = 0;

map.on('render', () => {
  frameCount++;
  console.log(`已渲染 ${frameCount} 帧`);
});
```

### idle - 空闲状态

```javascript
map.on('idle', () => {
  console.log('地图处于空闲状态(无动画、无加载)');
});
```

### error - 错误

```javascript
map.on('error', (e) => {
  console.error('地图错误:', e.error);
  
  // 记录错误日志
  logError({
    message: e.error.message,
    stack: e.error.stack,
    timestamp: new Date().toISOString()
  });
});
```

## 视图变化事件

### movestart / move / moveend - 移动

```javascript
map.on('movestart', () => {
  console.log('开始移动');
  showLoadingIndicator();
});

map.on('move', () => {
  const center = map.getCenter();
  const zoom = map.getZoom();
  console.log(`移动中: ${center.lng.toFixed(4)}, ${center.lat.toFixed(4)}, zoom: ${zoom}`);
});

map.on('moveend', () => {
  console.log('移动结束');
  hideLoadingIndicator();
  
  // 更新URL
  updateUrlHash();
  
  // 加载新数据
  loadDataForViewport();
});
```

### zoomstart / zoom / zoomend - 缩放

```javascript
map.on('zoomstart', () => {
  console.log('开始缩放');
});

map.on('zoom', () => {
  console.log('当前缩放:', map.getZoom());
});

map.on('zoomend', () => {
  console.log('缩放结束');
});
```

### rotatestart / rotate / rotateend - 旋转

```javascript
map.on('rotatestart', () => {
  console.log('开始旋转');
});

map.on('rotate', () => {
  console.log('当前角度:', map.getBearing());
});

map.on('rotateend', () => {
  console.log('旋转结束');
});
```

### pitchstart / pitch / pitchend - 俯仰

```javascript
map.on('pitchstart', () => {
  console.log('开始俯仰');
});

map.on('pitch', () => {
  console.log('当前俯仰角:', map.getPitch());
});

map.on('pitchend', () => {
  console.log('俯仰结束');
});
```

## 数据加载事件

### dataloading - 数据加载中

```javascript
map.on('dataloading', (e) => {
  console.log('数据类型加载中:', e.dataType);
  // dataType: 'source' | 'style'
});
```

### data - 数据加载

```javascript
map.on('data', (e) => {
  console.log('数据已加载:', e.dataType);
  
  if (e.isSourceLoaded) {
    console.log('所有源数据已加载');
  }
});
```

### sourcedataloading / sourcedata - 源数据

```javascript
map.on('sourcedataloading', (e) => {
  console.log('源数据加载中:', e.sourceId);
});

map.on('sourcedata', (e) => {
  console.log('源数据已加载:', e.sourceId);
  
  if (e.isSourceLoaded && e.sourceId === 'my-source') {
    console.log('我的数据源加载完成');
  }
});
```

### styledataloading / styledata - 样式数据

```javascript
map.on('styledataloading', () => {
  console.log('样式数据加载中');
});

map.on('styledata', () => {
  console.log('样式数据已加载');
});
```

## 图层事件

### layer.add / layer.remove - 图层添加/移除

```javascript
map.on('layer.add', (e) => {
  console.log('图层已添加:', e.layer.id);
});

map.on('layer.remove', (e) => {
  console.log('图层已移除:', e.layer.id);
});
```

## 实用示例

### 坐标显示

```javascript
const coordDisplay = document.getElementById('coordinates');

map.on('mousemove', (e) => {
  coordDisplay.textContent = 
    `经度: ${e.lngLat.lng.toFixed(6)}\n纬度: ${e.lngLat.lat.toFixed(6)}`;
});
```

### 要素信息查询

```javascript
map.on('click', (e) => {
  const features = map.queryRenderedFeatures(e.point);
  
  if (features.length > 0) {
    const properties = features[0].properties;
    
    new dmapgl.Popup()
      .setLngLat(e.lngLat)
      .setHTML(createInfoPopup(properties))
      .addTo(map);
  }
});

function createInfoPopup(properties) {
  let html = '<div class="info-popup">';
  for (const [key, value] of Object.entries(properties)) {
    html += `<p><strong>${key}:</strong> ${value}</p>`;
  }
  html += '</div>';
  return html;
}
```

### 绘制工具

```javascript
let isDrawing = false;
let drawPath = [];

map.on('mousedown', (e) => {
  isDrawing = true;
  drawPath = [[e.lngLat.lng, e.lngLat.lat]];
  
  // 创建临时图层
  map.addSource('draw-source', {
    type: 'geojson',
    data: {
      type: 'Feature',
      geometry: {
        type: 'LineString',
        coordinates: drawPath
      }
    }
  });
  
  map.addLayer({
    id: 'draw-layer',
    type: 'line',
    source: 'draw-source',
    paint: {
      'line-color': '#3b82f6',
      'line-width': 2
    }
  });
});

map.on('mousemove', (e) => {
  if (!isDrawing) return;
  
  drawPath.push([e.lngLat.lng, e.lngLat.lat]);
  
  const source = map.getSource('draw-source');
  source.setData({
    type: 'Feature',
    geometry: {
      type: 'LineString',
      coordinates: drawPath
    }
  });
});

map.on('mouseup', () => {
  isDrawing = false;
  console.log('绘制完成:', drawPath);
});
```

### 视口数据加载

```javascript
let isLoading = false;

async function loadDataForViewport() {
  if (isLoading) return;
  
  isLoading = true;
  const bounds = map.getBounds();
  
  try {
    const response = await fetch(`/api/data?bbox=${bounds.toArray()}`);
    const data = await response.json();
    
    if (map.getSource('viewport-data')) {
      map.getSource('viewport-data').setData(data);
    } else {
      map.addSource('viewport-data', {
        type: 'geojson',
        data: data
      });
      
      map.addLayer({
        id: 'viewport-layer',
        type: 'circle',
        source: 'viewport-data',
        paint: {
          'circle-radius': 5,
          'circle-color': '#3b82f6'
        }
      });
    }
  } catch (error) {
    console.error('加载数据失败:', error);
  } finally {
    isLoading = false;
  }
}

map.on('moveend', loadDataForViewport);
```

### 性能监控

```javascript
const metrics = {
  renders: 0,
  moves: 0,
  errors: 0
};

map.on('render', () => {
  metrics.renders++;
});

map.on('move', () => {
  metrics.moves++;
});

map.on('error', () => {
  metrics.errors++;
});

// 定期上报
setInterval(() => {
  console.log('性能指标:', metrics);
  // sendToAnalytics(metrics);
}, 60000);
```

### 历史记录

```javascript
const viewHistory = [];

map.on('moveend', () => {
  viewHistory.push({
    center: map.getCenter(),
    zoom: map.getZoom(),
    bearing: map.getBearing(),
    pitch: map.getPitch(),
    timestamp: Date.now()
  });
  
  // 保持最近50条记录
  if (viewHistory.length > 50) {
    viewHistory.shift();
  }
});

// 后退功能
function goBack() {
  if (viewHistory.length > 1) {
    viewHistory.pop(); // 移除当前视图
    const previous = viewHistory[viewHistory.length - 1];
    
    map.flyTo({
      center: previous.center,
      zoom: previous.zoom,
      bearing: previous.bearing,
      pitch: previous.pitch,
      duration: 1000
    });
  }
}
```

## 事件对象属性

### MapMouseEvent

```javascript
{
  type: 'click',              // 事件类型
  target: Map,                // 地图实例
  originalEvent: MouseEvent,  // 原始DOM事件
  point: Point,               // 像素坐标 {x, y}
  lngLat: LngLat,             // 地理坐标 {lng, lat}
  features: Array             // 要素数组(仅图层事件)
}
```

### MapTouchEvent

```javascript
{
  type: 'touchstart',
  target: Map,
  originalEvent: TouchEvent,
  points: Array,              // 触摸点数组
  lngLats: Array              // 地理坐标数组
}
```

### DataEvent

```javascript
{
  type: 'data',
  target: Map,
  dataType: 'source',         // 'source' | 'style'
  isSourceLoaded: boolean,
  sourceId: string            // 源ID(仅source类型)
}
```

## 注意事项

1. **性能**: 避免在高频事件(move, render)中执行耗时操作
2. **内存泄漏**: 组件卸载时解绑所有事件监听器
3. **事件冒泡**: 使用 `stopPropagation()` 阻止事件传播
4. **异步处理**: 事件处理器中的异步操作注意错误处理
5. **节流防抖**: 对高频事件使用节流或防抖
6. **图层事件**: 确保图层存在后再绑定事件
7. **错误处理**: 始终监听 error 事件


path: references/map-init.md
content: 

# 地图初始化与生命周期

> **坐标系说明**: DMap 使用地方平面坐标系，中心点为 `[800000, 600000]`，地图范围为 `[716638.24, 548483.59, 894455.08, 728066.47]`，缩放级别范围为 `7-19`。

## Map 构造函数

创建一个新的 DMap GL 地图实例。

```javascript
var map = new dmapgl.Map(options);
```

### 完整参数

| 参数 | 类型                                | 默认值 | 说明 |
|------|-----------------------------------|--------|------|
| antialias | `boolean`                         | `false` | 是否启用MSAA抗锯齿。默认为false以优化性能 |
| attributionControl | `boolean`                         | `true` | 是否添加归属控件(AttributionControl) |
| bearing | `number`                          | `0` | 初始方位角(旋转角度),从北方逆时针测量(度)。如果未在选项中指定,将从样式对象中查找 |
| bearingSnap | `number`                          | `7` | 方位角吸附阈值(度)。当旋转到北方7度范围内时,地图会自动吸附到正北 |
| bounds | `LngLatBoundsLike`                | `null` | 初始边界。如果指定,将覆盖 center 和 zoom 选项 |
| boxZoom | `boolean`                         | `true` | 是否启用"框选缩放"交互(BoxZoomHandler) |
| center | `LngLatLike`                      | `[0, 0]` | 初始地理中心点 [经度, 纬度]。注意:DMap使用地方平面坐标系 |
| clickTolerance | `number`                          | `3` | 点击期间鼠标可移动的最大像素数,超过则视为拖拽而非点击 |
| collectResourceTiming | `boolean`                         | `false` | 是否收集GeoJSON和矢量瓦片Web Worker的资源时序API信息 |
| config | `Object`                          | `null` | 样式片段的初始配置选项。每个键是片段ID(如 basemap),值是配置对象 |
| container | `HTMLElement / string`            | - | **必需**。HTML容器元素或其字符串ID。指定元素必须无子元素 |
| cooperativeGestures | `boolean`                         | - | 协作手势模式。启用后,滚轮缩放需按住Ctrl/⌘键,触摸平移需双指,触摸俯仰需三指 |
| crossSourceCollisions | `boolean`                         | `true` | 多源符号是否相互碰撞检测。false则各源独立进行碰撞检测 |
| customAttribution | `string / Array<string>`          | `null` | 在归属控件中显示的自定义文本。仅在 attributionControl 为 true 时有效 |
| doubleClickZoom | `boolean`                         | `true` | 是否启用"双击缩放"交互(DoubleClickZoomHandler) |
| dragPan | `boolean / Object`                | `true` | 是否启用"拖拽平移"交互。可传入对象作为 DragPanHandler#enable 的选项 |
| dragRotate | `boolean`                         | `true` | 是否启用"拖拽旋转"交互(DragRotateHandler) |
| fadeDuration | `number`                          | `300` | 标签碰撞淡入/淡出动画持续时间(毫秒)。影响所有符号图层 |
| failIfMajorPerformanceCaveat | `boolean`                         | `false` | 如果性能远低于预期(使用软件渲染),是否让地图创建失败 |
| fitBoundsOptions | `Object`                          | `null` | 仅在适配初始 bounds 时使用的 Map#fitBounds 选项对象 |
| hash | `boolean / string`                | `false` | 是否将地图位置(zoom、center、bearing、pitch)与URL哈希同步。可传入字符串作为自定义参数名 |
| interactive | `boolean`                         | `true` | 是否响应交互。false则不附加任何鼠标、触摸或键盘监听器 |
| keyboard | `boolean`                         | `true` | 是否启用键盘快捷键(KeyboardHandler) |
| language | `"auto" / string / Array<string>` | `null` | BCP 47语言标签,用于地图标签和UI组件。设为 "auto" 时使用浏览器语言 |
| locale | `Object`                          | `null` | UI字符串本地化补丁。映射命名空间UI字符串ID到目标语言的翻译 |
| localFontFamily | `string`                          | `null` | CSS字体族,用于本地覆盖所有字形生成。设置后将忽略样式中的字体设置(除字重外) |
| localIdeographFontFamily | `string`                          | `'sans-serif'` | CSS字体族,用于本地覆盖CJK统一表意文字等范围的字形。设为 false 以使用样式中的字体设置 |
| logoPosition | `string`                          | `'bottom-left'` | 标识位置。可选: `top-left`、`top-right`、`bottom-left`、`bottom-right` |
| maxBounds | `LngLatBoundsLike`                | `null` | 地图约束边界。设置后地图将被限制在此范围内 |
| maxPitch | `number`                          | `85` | 最大俯仰角(0-85度) |
| maxTileCacheSize | `number`                          | `null` | 每个源的最大瓦片缓存数量。省略则根据视口动态调整 |
| maxZoom | `number`                          | `22` | 最大缩放级别(0-24) |
| minPitch | `number`                          | `0` | 最小俯仰角(0-85度) |
| minTileCacheSize | `number`                          | `null` | 每个源的最小瓦片缓存数量。省略则根据视口动态调整 |
| minZoom | `number`                          | `0` | 最小缩放级别(0-24) |
| performanceMetricsCollection | `boolean`                         | `true` | 是否收集和发送性能指标 |
| pitch | `number`                          | `0` | 初始俯仰角(倾斜角),距屏幕平面的角度(0-85度)。如果未指定,将从样式对象中查找 |
| pitchWithRotate | `boolean`                         | `true` | 俯仰是否与旋转联动。false则禁用两指旋转时的俯仰控制 |
| preserveDrawingBuffer | `boolean`                         | `false` | 是否保留绘图缓冲区。true时可使用 canvas.toDataURL() 导出PNG。默认为false以优化性能 |
| refreshExpiredTiles | `boolean`                         | `true` | 是否刷新过期的瓦片 |
| renderWorldCopies | `boolean`                         | `true` | 是否渲染世界副本(横向重复)。false则只显示一个世界 |
| scrollZoom | `boolean / Object`                | `true` | 是否启用"滚轮缩放"交互。可传入对象作为 ScrollZoomHandler#enable 的选项 |
| style | `string / Object`                 | - | **必需**。地图样式URL或样式对象 |
| touchPitch | `boolean / Object`                | `true` | 是否启用"触摸俯仰"交互。可传入对象作为 TouchPitchHandler#enable 的选项 |
| touchZoomRotate | `boolean / Object`                | `true` | 是否启用"触摸缩放旋转"交互。可传入对象作为 TouchZoomRotateHandler#enable 的选项 |
| trackResize | `boolean`                         | `true` | 是否跟踪容器尺寸变化并自动调整地图大小 |
| transformRequest | `Function`                        | `null` | 请求转换函数。可在发送请求前修改URL、headers等。签名: `(url: string, resourceType: string) => RequestParameters` |

### 示例

```javascript
// 基础初始化
// 注意：此处需改为你的后台服务地址
dmapgl.serviceUrl = 'http://172.26.64.84/api';
var map = new dmapgl.Map({
  container: 'map',
  style: dmapgl.serviceUrl + '/map/vector/standard/styles/style.json',
  center: [800000, 600000], // 地方平面坐标
  zoom: 11,
  minZoom: 7,
  maxZoom: 19,
  maxBounds: [
    [716638.2414098255, 548483.5939021005],
    [894455.0756895209, 728066.4667000007]
  ]
});

// 高级配置
var map = new dmapgl.Map({
  container: 'map',
  style: dmapgl.serviceUrl + '/map/vector/standard/styles/style.json',
  center: [800000, 600000],
  zoom: 13,
  pitch: 60,
  bearing: -17.6,
  antialias: true,
  minZoom: 7,
  maxZoom: 19
});
```

## 地图事件

### load

地图样式加载完成时触发。

```javascript
map.on('load', () => {
  console.log('地图加载完成');
  // 在此添加图层、数据源等
});
```

### render

地图渲染帧时触发(高频)。

```javascript
map.on('render', () => {
  // 用于动画或持续更新
});
```

### error

发生错误时触发。

```javascript
map.on('error', (e) => {
  console.error('地图错误:', e.error);
});
```

## 地图销毁

组件卸载时必须调用,释放WebGL资源。

```javascript
// 方法1: remove() - 移除地图并清理
map.remove();

// 方法2: destroy() - 仅清理内部状态
map.destroy();
```

## 等待地图就绪

### 使用 load 事件

```javascript
map.on('load', () => {
  // 安全地添加图层和数据源
  map.addSource('points', {
    type: 'geojson',
    data: features
  });
});
```

### 检查 loaded 状态

```javascript
if (map.loaded()) {
  // 地图已完全加载
  map.addLayer({ /* ... */ });
} else {
  map.on('load', () => {
    map.addLayer({ /* ... */ });
  });
}
```

## 样式切换

动态切换地图样式。

```javascript
// 切换样式
map.setStyle('zyzx://vector/black/styles/style.json');

// 监听样式加载完成
map.on('style.load', () => {
  console.log('新样式加载完成');
  // 重新添加自定义图层
});
```

## 完整示例

```javascript

// 注意：此处需改为你的后台服务地址
dmapgl.serviceUrl = 'http://172.26.64.84/api';

// 创建地图
var map = new dmapgl.Map({
  container: 'map-container',
  style: dmapgl.serviceUrl + '/map/vector/standard/styles/style.json',
  center: [800000, 600000],
  zoom: 11,
  pitch: 45,
  minZoom: 7,
  maxZoom: 19,
  antialias: true
});

// 等待加载
map.on('load', () => {
  console.log('地图就绪');
  
  // 添加控件
  map.addControl(new dmapgl.NavigationControl());
  map.addControl(new dmapgl.ScaleControl());
});

// 错误处理
map.on('error', (e) => {
  console.error('地图错误:', e.error);
});

// 清理函数
function cleanup() {
  map.remove();
}
```

## 注意事项

1. **容器要求**: 容器元素必须存在且无子元素
2. **CSS尺寸**: 确保容器有明确的宽高
3. **异步加载**: 所有图层操作应在 `load` 事件后进行
4. **资源清理**: 务必在组件销毁时调用 `remove()`


path: references/api/map.md
content: 

# Map - 地图核心对象

Map是DMap GL的核心类,代表页面上的地图实例。

## 构造函数

```javascript
new dmapgl.Map(options)
```

详见 [地图初始化](../map-init.md)

## 实例属性 (Handlers)

Map实例提供以下交互处理器属性,用于控制用户交互:

- `boxZoom` - BoxZoomHandler 框选缩放处理器
- `scrollZoom` - ScrollZoomHandler 滚轮缩放处理器
- `dragPan` - DragPanHandler 拖拽平移处理器
- `dragRotate` - DragRotateHandler 拖拽旋转处理器
- `keyboard` - KeyboardHandler 键盘交互处理器
- `doubleClickZoom` - DoubleClickZoomHandler 双击缩放处理器
- `touchZoomRotate` - TouchZoomRotateHandler 触摸缩放旋转处理器
- `twoFingersTouchPitch` - TwoFingersTouchPitchHandler 双指倾斜处理器
- `twoFingersTouchZoomRotate` - TwoFingersTouchZoomRotateHandler 双指缩放旋转处理器

每个handler都有以下方法:
- `enable()` - 启用该交互
- `disable()` - 禁用该交互
- `isEnabled()` - 检查是否启用
- `isActive()` - 检查是否正在活动(仅部分handler)

```javascript
// 禁用滚轮缩放
map.scrollZoom.disable();

// 禁用框选缩放
map.boxZoom.disable();

// 禁用拖拽旋转
map.dragRotate.disable();

// 检查状态
if (map.keyboard.isEnabled()) {
  console.log('键盘交互已启用');
}
```

## 实例方法

### Camera - 相机控制

#### 获取当前视图状态

- `getCenter()` - 获取中心点坐标
- `getZoom()` - 获取缩放级别
- `getBearing()` - 获取旋转角度(方位角)
- `getPitch()` - 获取俯仰角
- `getPadding()` - 获取填充
- `getBounds()` - 获取可视区域边界
- `project(lnglat)` - 地理坐标转屏幕像素坐标
- `unproject(point)` - 屏幕像素坐标转地理坐标

#### 设置视图状态

- `setCenter(center)` - 设置中心点
- `setZoom(zoom)` - 设置缩放级别
- `setBearing(bearing)` - 设置旋转角度
- `setPitch(pitch)` - 设置俯仰角
- `setPadding(padding)` - 设置填充

#### 动画过渡

- `jumpTo(options)` - 立即跳转到指定视图(无动画)
- `easeTo(options)` - 平滑过渡到指定视图
- `flyTo(options)` - 飞行动画到指定视图
- `zoomTo(zoom, options)` - 缩放到指定级别
- `panTo(lnglat, options)` - 平移到指定位置
- `rotateTo(bearing, options)` - 旋转到指定角度
- `pitchTo(pitch, options)` - 倾斜到指定角度
- `fitBounds(bounds, options)` - 适配到指定边界
- `fitScreenCoordinates(p0, p1, bearing, options)` - 适配屏幕坐标
- `stop()` - 停止所有正在进行的动画和过渡

#### 查询动画状态

- `isMoving()` - 检查地图是否正在移动
- `isZooming()` - 检查地图是否正在缩放
- `isRotating()` - 检查地图是否正在旋转

### Map constraints - 地图约束

#### 地图约束

- `setMaxBounds(bounds)` - 设置最大边界限制
- `getMaxBounds()` - 获取最大边界
- `setMinZoom(minZoom)` - 设置最小缩放级别
- `getMinZoom()` - 获取最小缩放级别
- `setMaxZoom(maxZoom)` - 设置最大缩放级别
- `getMaxZoom()` - 获取最大缩放级别
- `setMinPitch(minPitch)` - 设置最小俯仰角
- `getMinPitch()` - 获取最小俯仰角
- `setMaxPitch(maxPitch)` - 设置最大俯仰角
- `getMaxPitch()` - 获取最大俯仰角
- `setRenderWorldCopies(boolean)` - 设置是否渲染世界副本
- `getRenderWorldCopies()` - 获取是否渲染世界副本

### Markers and controls - 标记和控件

- `addControl(control, position)` - 添加控件到地图
- `removeControl(control)` - 从地图移除控件

### Styling - 样式管理

#### 图层管理

- `addLayer(layer, beforeId)` - 添加图层
- `removeLayer(id)` - 移除图层
- `moveLayer(id, beforeId)` - 移动图层到指定位置
- `getLayer(id)` - 获取图层配置

#### 图层属性

- `setLayoutProperty(layerId, name, value)` - 设置布局属性
- `getLayoutProperty(layerId, name)` - 获取布局属性
- `setPaintProperty(layerId, name, value)` - 设置绘制属性
- `getPaintProperty(layerId, name)` - 获取绘制属性
- `setFilter(layerId, filter)` - 设置过滤器
- `getFilter(layerId)` - 获取过滤器
- `setFeatureState(source, sourceLayer, featureId, state)` - 设置要素状态
- `getFeatureState(source, sourceLayer, featureId)` - 获取要素状态
- `removeFeatureState(target, key)` - 移除要素状态

#### 数据源管理

- `addSource(id, source)` - 添加数据源
- `removeSource(id)` - 移除数据源
- `getSource(id)` - 获取数据源
- `isSourceLoaded(id)` - 检查数据源是否加载完成

#### 图片管理

- `addImage(name, image, options)` - 添加图片
- `updateImage(name, image)` - 更新图片
- `removeImage(name)` - 移除图片
- `hasImage(name)` - 检查图片是否存在
- `getImage(name)` - 获取图片
- `loadImage(url, callback)` - 加载图片
- `listImages()` - 列出所有图片

#### 样式整体操作

- `setStyle(style, options)` - 设置完整样式
- `getStyle()` - 获取当前样式
- `areTilesLoaded()` - 检查瓦片是否全部加载

#### 光照、地形、雾效、投影

- `setLight(options)` - 设置光照
- `getLight()` - 获取光照配置
- `setTerrain(options)` - 设置地形
- `getTerrain()` - 获取地形配置
- `setFog(options)` - 设置雾效
- `getFog()` - 获取雾效配置
- `setProjection(projection)` - 设置投影方式
- `getProjection()` - 获取投影配置

### Querying features - 查询要素

- `queryRenderedFeatures(geometryOrPoint, options)` - 查询渲染的要素
- `querySourceFeatures(sourceId, parameters)` - 查询数据源要素
- `queryTerrainElevation(lngLat, options)` - 查询地形高程

### I/O - 输入输出

#### 容器和Canvas

- `getContainer()` - 获取地图容器DOM元素
- `getCanvasContainer()` - 获取Canvas容器DOM元素
- `getCanvas()` - 获取Canvas元素

#### 显示选项(属性)

- `showTileBoundaries` - 显示瓦片边界
- `showCollisionBoxes` - 显示碰撞检测框
- `showPadding` - 显示填充区域
- `repaint` - 强制重绘
- `showOverdrawInspector` - 显示过度绘制检查器

#### 数据管理

- `refreshTiles()` - 刷新瓦片
- `clearData(options, callback)` - 清除缓存数据
- `getPixelRatio()` - 获取像素比
- `setPixelRatio(ratio)` - 设置像素比
- `hasControl(control)` - 检查控件是否存在

### Other - 其他方法

#### 生命周期

- `loaded()` - 检查地图是否完全加载
- `remove()` - 移除地图并清理资源
- `destroy()` - 销毁地图实例(同remove)
- `triggerRepaint()` - 触发重绘

#### 尺寸调整

- `resize()` - 调整地图大小以适应容器
- `update()` - 更新地图状态

#### 瓦片管理

- `reloadSource(sourceId)` - 重新加载指定数据源

## 完整示例

```javascript
// 注意：此处需改为你的后台服务地址
dmapgl.serviceUrl = 'http://172.26.64.84/api';
var map = new dmapgl.Map({
  container: 'map',
  style: dmapgl.serviceUrl + '/map/vector/standard/styles/style.json',
  center: [800000, 600000],
  zoom: 11,
  pitch: 0,
  bearing: 0
});

map.on('load', () => {
  // 添加图层
  map.addLayer({
    id: 'points',
    type: 'circle',
    source: {
      type: 'geojson',
      data: { /* ... */ }
    }
  });
  
  // 添加控件
  map.addControl(new dmapgl.NavigationControl(), 'top-right');
  map.addControl(new dmapgl.ScaleControl(), 'bottom-left');
  
  // 禁用某些交互
  map.dragRotate.disable();
  map.touchZoomRotate.disableRotation();
  
  // 绑定事件
  map.on('click', 'points', (e) => {
    console.log('点击了要素:', e.features);
  });
  
  // 飞行动画
  map.flyTo({
    center: [850000, 650000],
    zoom: 14,
    essential: true
  });
});

// 获取地图信息
console.log('中心点:', map.getCenter());
console.log('缩放级别:', map.getZoom());
console.log('Canvas:', map.getCanvas());
console.log('容器:', map.getContainer());

// 设置地图约束
map.setMinZoom(7);
map.setMaxZoom(19);
map.setMaxBounds([
  [716638.24, 548483.59],
  [894455.08, 728066.47]
]);
```


path: references/api/sources.md
content: 

# Sources - 数据源

数据源定义地图上要显示的地理数据。

## GeoJSONSource

GeoJSON格式数据源。

### 配置选项

- `type: 'geojson'` - 类型
- `data` - GeoJSON数据或URL
- `maxzoom` - 最大缩放
- `buffer` - 瓦片缓冲
- `tolerance` - 容差
- `cluster` - 启用聚类
- `clusterMaxZoom` - 聚类最大缩放
- `clusterRadius` - 聚类半径
- `lineMetrics` - 行度量
- `generateId` - 生成ID
- `promoteId` - 提升ID

### 实例方法

- `setData(data)` - 更新数据
- `updateData(data)` - 增量更新
- `getClusterExpansionZoom(clusterId, callback)` - 获取聚类展开缩放
- `getClusterChildren(clusterId, callback)` - 获取聚类子要素
- `getClusterLeaves(clusterId, limit, offset, callback)` - 获取聚类叶子节点

详见 [数据源文档](../sources.md)

## VectorTileSource

矢量瓦片数据源。

### 配置选项

- `type: 'vector'` - 类型
- `url` - TileJSON URL
- `tiles` - 瓦片URL模板
- `minzoom` - 最小缩放
- `maxzoom` - 最大缩放
- `tileSize` - 瓦片尺寸
- `scheme` - 方案(xyz/tms)
- `bounds` - 边界
- `attribution` - 版权信息

### 实例方法

- `reload()` - 重新加载瓦片
- `setTiles(tiles)` - 设置瓦片URL数组
- `setUrl(url)` - 设置TileJSON URL

详见 [数据源文档](../sources.md)

## RasterTileSource

栅格瓦片数据源。

### 配置选项

- `type: 'raster'` - 类型
- `url` - TileJSON URL
- `tiles` - 瓦片URL模板
- `minzoom` - 最小缩放
- `maxzoom` - 最大缩放
- `tileSize` - 瓦片尺寸(256/512)
- `scheme` - 方案
- `bounds` - 边界
- `attribution` - 版权信息

### 实例方法

- `reload()` - 重新加载瓦片
- `setTiles(tiles)` - 设置瓦片URL数组
- `setUrl(url)` - 设置TileJSON URL

详见 [数据源文档](../sources.md)

## RasterArrayTileSource

栅格数组瓦片数据源(Mapbox Tiling Service创建)。

### 配置选项

- `type: 'raster-array'` - 类型
- `url` - TileJSON URL
- `tileSize` - 瓦片尺寸

### 实例方法

- `reload()` - 重新加载瓦片
- `setTiles(tiles)` - 设置瓦片URL数组
- `setUrl(url)` - 设置TileJSON URL

详见 [数据源文档](../sources.md)

## ImageSource

单张图片数据源。

### 配置选项

- `type: 'image'` - 类型
- `url` - 图片URL
- `coordinates` - 四角坐标 [[左上], [右上], [右下], [左下]]

### 实例方法

- `updateImage(options)` - 更新图片和坐标
- `setCoordinates(coordinates)` - 更新坐标

详见 [数据源文档](../sources.md)

## VideoSource

视频数据源。

### 配置选项

- `type: 'video'` - 类型
- `urls` - 视频URL数组
- `coordinates` - 四角坐标

### 实例方法

- `getVideo()` - 获取视频元素
- `play()` - 播放
- `pause()` - 暂停
- `setCoordinates(coordinates)` - 更新坐标

详见 [数据源文档](../sources.md)

## CanvasSource

Canvas数据源。

### 配置选项

- `type: 'canvas'` - 类型
- `canvas` - Canvas元素或ID
- `animate` - 是否动画
- `coordinates` - 四角坐标

### 实例方法

- `play()` - 开始动画
- `pause()` - 暂停动画
- `getCanvas()` - 获取Canvas元素
- `setCoordinates(coordinates)` - 更新坐标

详见 [数据源文档](../sources.md)


## 使用示例

```javascript
// GeoJSON源
map.addSource('points', {
  type: 'geojson',
  data: geoJsonData,
  cluster: true
});

// 矢量瓦片源
map.addSource('custom-vector', {
    type: 'vector',
    tiles: [
        'https://example.com/tiles/{z}/{x}/{y}.pbf'
    ],
    minzoom: 7,
    maxzoom: 14
});

// 栅格瓦片源
map.addSource('custom-raster', {
    type: 'raster',
    tiles: [
        'https://example.com/tiles/{z}/{x}/{y}.png'
    ],
    tileSize: 512
});

// 图片源
map.addSource('overlay', {
  type: 'image',
  url: 'image.png',
  coordinates: [
    [-76.54, 39.18],
    [-76.52, 39.18],
    [-76.52, 39.17],
    [-76.54, 39.17]
  ]
});

// 动态更新
const source = map.getSource('points');
source.setData(newData);
```


path: references/api/markers-controls.md
content: 

# Markers and Controls - 标记和控件

用户界面元素,可以在运行时添加到地图上。这些元素存在于地图的 canvas 元素之外。

## IControl - 控件接口

所有控件必须实现的接口规范。

### 必需方法

- `onAdd(map)` - 添加控件时调用,返回 DOM 元素
- `onRemove()` - 移除控件时调用
- `getDefaultPosition()` - 获取默认位置(可选)

### 自定义控件示例

```javascript
class CustomControl {
  onAdd(map) {
    this._map = map;
    this._container = document.createElement('div');
    this._container.className = 'mapboxgl-ctrl';
    this._container.textContent = 'Hello, world';
    return this._container;
  }
  
  onRemove() {
    this._container.parentNode.removeChild(this._container);
    this._map = undefined;
  }
}

map.addControl(new CustomControl(), 'top-right');
```

## Marker - 标记

在地图上添加点标记。

### 构造函数

```javascript
new dmapgl.Marker(options)
```

### 配置选项

- `element` - 自定义DOM元素
- `color` - 标记颜色 (默认 '#3FB1CE')
- `scale` - 缩放比例 (默认 1)
- `anchor` - 锚点位置 ('center', 'top', 'bottom', 'left', 'right'等)
- `offset` - 偏移量 [x, y]
- `draggable` - 是否可拖拽 (默认 false)
- `clickTolerance` - 点击容差 (默认 0)
- `rotation` - 旋转角度 (默认 0)
- `pitchAlignment` - 俯仰对齐方式 ('map', 'viewport', 'auto')
- `rotationAlignment` - 旋转对齐方式 ('map', 'viewport', 'auto')

### 实例方法

#### 基本操作

- `addTo(map)` - 添加到地图
- `remove()` - 从地图移除
- `getMap()` - 获取地图实例
- `getElement()` - 获取DOM元素

#### 位置和外观

- `getLngLat()` - 获取位置坐标
- `setLngLat(lnglat)` - 设置位置坐标
- `getOffset()` - 获取偏移量
- `setOffset(offset)` - 设置偏移量
- `getColor()` - 获取颜色
- `setColor(color)` - 设置颜色
- `getScale()` - 获取缩放比例
- `setScale(scale)` - 设置缩放比例
- `getRotation()` - 获取旋转角度
- `setRotation(rotation)` - 设置旋转角度
- `getRotationAlignment()` - 获取旋转对齐方式
- `setRotationAlignment(alignment)` - 设置旋转对齐方式
- `getPitchAlignment()` - 获取俯仰对齐方式
- `setPitchAlignment(alignment)` - 设置俯仰对齐方式
- `getAnchor()` - 获取锚点
- `setAnchor(anchor)` - 设置锚点

#### 拖拽

- `getDraggable()` - 获取是否可拖拽
- `setDraggable(boolean)` - 设置是否可拖拽
- `getElement()` - 获取DOM元素

#### 弹窗

- `getPopup()` - 获取关联的弹窗
- `setPopup(popup)` - 设置关联的弹窗
- `togglePopup()` - 切换弹窗显示/隐藏
- `getOffset()` - 获取弹窗偏移量

### 事件

- `dragstart` - 开始拖拽
- `drag` - 拖拽中
- `dragend` - 结束拖拽

详见 [标记文档](../marker.md)

## Popup - 弹窗

显示信息弹窗。

### 构造函数

```javascript
new dmapgl.Popup(options)
```

### 配置选项

- `closeButton` - 是否显示关闭按钮 (默认 true)
- `closeOnClick` - 点击地图关闭 (默认 true)
- `closeOnEscape` - ESC键关闭 (默认 true)
- `closeOnMove` - 移动时关闭 (默认 false)
- `focusAfterOpen` - 打开后聚焦 (默认 true)
- `offset` - 偏移量 (默认 Point(0, 0))
- `maxWidth` - 最大宽度 (默认 '240px')
- `className` - CSS类名
- `anchor` - 锚点位置

### 实例方法

#### 基本操作

- `addTo(map)` - 添加到地图
- `remove()` - 从地图移除
- `isOpen()` - 检查是否打开
- `remove()` - 移除弹窗

#### 位置和内容

- `getLngLat()` - 获取位置
- `setLngLat(lnglat)` - 设置位置
- `getHTML()` - 获取HTML内容
- `setHTML(html)` - 设置HTML内容
- `getText()` - 获取文本内容
- `setText(text)` - 设置文本内容
- `getDOMContent()` - 获取DOM内容
- `setDOMContent(element)` - 设置DOM内容

#### 样式和布局

- `getOffset()` - 获取偏移量
- `setOffset(offset)` - 设置偏移量
- `getMaxWidth()` - 获取最大宽度
- `setMaxWidth(width)` - 设置最大宽度
- `getAnchor()` - 获取锚点
- `setAnchor(anchor)` - 设置锚点
- `addClassName(className)` - 添加CSS类名
- `removeClassName(className)` - 移除CSS类名
- `toggleClassName(className)` - 切换CSS类名
- `getClassName()` - 获取CSS类名

#### 其他

- `trackPointer()` - 跟踪鼠标指针
- `getTrackPointer()` - 检查是否跟踪指针
- `getElement()` - 获取弹窗DOM元素

### 事件

- `open` - 弹窗打开
- `close` - 弹窗关闭

详见 [弹窗文档](../popup.md)

## NavigationControl - 导航控件

缩放和旋转控制按钮。

### 构造函数

```javascript
new dmapgl.NavigationControl(options)
```

### 配置选项

- `showZoom` - 显示缩放按钮 (默认 true)
- `showCompass` - 显示指南针 (默认 true)
- `visualizePitch` - 显示俯仰指示器 (默认 false)

### 事件

- `rotate` - 旋转时触发
- `pitch` - 倾斜时触发

详见 [控件文档](../controls.md)


## ScaleControl - 比例尺控件

显示地图比例。

### 构造函数

```javascript
new dmapgl.ScaleControl(options)
```

### 配置选项

- `maxWidth` - 最大宽度 (默认 100)
- `unit` - 单位 ('imperial', 'metric', 'nautical')

### 实例方法

- `setUnit(unit)` - 设置单位

详见 [控件文档](../controls.md)

## FullscreenControl - 全屏控件

切换全屏显示。

### 构造函数

```javascript
new dmapgl.FullscreenControl(options)
```

### 配置选项

- `container` - 全屏容器元素 (默认地图容器)

### 事件

- `fullscreenchange` - 全屏状态变化

详见 [控件文档](../controls.md)

## AttributionControl - 版权控件

显示数据来源版权。

### 构造函数

```javascript
new dmapgl.AttributionControl(options)
```

### 配置选项

- `compact` - 紧凑模式 (默认响应式)
- `customAttribution` - 自定义版权文本 (字符串或数组)

详见 [控件文档](../controls.md)

## 使用示例

### 创建标记和弹窗

```javascript
// 创建标记
const marker = new dmapgl.Marker({ 
  color: '#3b82f6',
  draggable: true,
  rotation: 45
})
  .setLngLat([800000, 600000])
  .addTo(map);

// 创建弹窗
const popup = new dmapgl.Popup({ 
  offset: 25,
  closeButton: true,
  maxWidth: '300px'
})
  .setHTML('<h3>北京</h3><p>这是北京市中心</p>');

// 关联弹窗到标记
marker.setPopup(popup);

// 监听拖拽事件
marker.on('dragend', () => {
  const lngLat = marker.getLngLat();
  console.log('新位置:', lngLat);
});
```

### 添加多个控件

```javascript
// 添加导航控件
map.addControl(
  new dmapgl.NavigationControl({
    showZoom: true,
    showCompass: true,
    visualizePitch: true
  }), 
  'top-right'
);

// 添加比例尺
map.addControl(
  new dmapgl.ScaleControl({
    maxWidth: 80,
    unit: 'metric'
  }), 
  'bottom-left'
);

// 添加地理定位
map.addControl(
  new dmapgl.GeolocateControl({
    positionOptions: { 
      enableHighAccuracy: true,
      timeout: 6000
    },
    trackUserLocation: true,
    showUserHeading: true
  }), 
  'top-right'
);

// 添加全屏控件
map.addControl(
  new dmapgl.FullscreenControl(),
  'top-right'
);

// 添加版权控件
map.addControl(
  new dmapgl.AttributionControl({
    compact: true,
    customAttribution: 'Map design by Me'
  }),
  'bottom-right'
);
```

### 自定义控件

```javascript
class ZoomInfoControl {
  onAdd(map) {
    this._map = map;
    this._container = document.createElement('div');
    this._container.className = 'mapboxgl-ctrl mapboxgl-ctrl-group';
    this._container.innerHTML = '<div id="zoom-info">Zoom: ' + map.getZoom() + '</div>';
    
    // 监听缩放变化
    map.on('zoom', () => {
      this._container.innerHTML = '<div id="zoom-info">Zoom: ' + Math.round(map.getZoom() * 100) / 100 + '</div>';
    });
    
    return this._container;
  }
  
  onRemove() {
    this._container.parentNode.removeChild(this._container);
    this._map = undefined;
  }
}

map.addControl(new ZoomInfoControl(), 'bottom-right');
```


path: references/api/properties.md
content: 

# Properties - 全局属性和工具函数

dmapgl命名空间下的全局属性、工具函数和浏览器检测。

## 后台服务地址

指定后台服务地址。

### 设置

```javascript
// 注意：此处需改为你的后台服务地址
dmapgl.serviceUrl = 'http://172.26.64.84/api';
```


## version - 版本号

获取DMap GL的版本号。

```javascript
console.log('DMap GL版本:', dmapgl.version);
// 例如: "1.0.0"
```

## supported - 浏览器支持检测

检测浏览器是否支持DMap GL。

### 基本检测

```javascript
if (!dmapgl.supported()) {
  alert('您的浏览器不支持DMap GL');
}
```

### 详细检测

```javascript
const support = dmapgl.supported({ failIfMajorPerformanceCaveat: true });

if (!support) {
  console.error('浏览器不支持WebGL或性能不足');
}
```

### 配置选项

```javascript
dmapgl.supported({
  failIfMajorPerformanceCaveat: false  // 默认false
});
```

## getRTLTextPlugin - 获取RTL文本插件

获取从右到左文本渲染插件的状态。

```javascript
const pluginUrl = dmapgl.getRTLTextPlugin();
console.log('RTL插件URL:', pluginUrl);
```

## setRTLTextPlugin - 设置RTL文本插件

设置从右到左文本（阿拉伯语、希伯来语等）渲染插件。

### 配置

```javascript
dmapgl.setRTLTextPlugin(
  'https://api.mapbox.com/mapbox-gl-js/plugins/mapbox-gl-rtl-text/v0.2.3/mapbox-gl-rtl-text.js',
  (error) => {
    if (error) {
      console.error('RTL插件加载失败:', error);
    } else {
      console.log('RTL插件加载成功');
    }
  },
  false  // lazy加载
);
```

### 参数说明

- **url**: 插件URL
- **callback**: 加载完成回调
- **lazy**: 是否延迟加载（默认false）


## clearStorage - 清除浏览器存储

清除库使用的浏览器存储,包括瓦片缓存。

```javascript
dmapgl.clearStorage((error) => {
  if (error) {
    console.error('清除存储失败:', error);
  } else {
    console.log('存储已清除');
  }
});
```

**注意**: 仅在支持 Cache API 的浏览器中可用(需要HTTPS)。

## clearPrewarmedResources - 清除预热资源

清除由 prewarm() 创建的资源。

```javascript
// 通常在不需要再使用地图时调用
dmapgl.clearPrewarmedResources();
```

## config - 全局配置

配置全局选项。

```javascript
dmapgl.config = {
  API_URL: 'https://api.dmap.com',
  MAX_PARALLEL_IMAGE_REQUESTS: 16
};
```

## WorkerCount - Worker数量

设置Web Worker数量。

```javascript
// 默认值为navigator.hardwareConcurrency或2
console.log('Worker数量:', dmapgl.workerCount);

// 自定义（必须在创建Map之前设置）
dmapgl.workerCount = 4;
```

## AnimationOptions - 动画选项

控制地图动画的通用选项。

### 属性

- `animate` - 是否启用动画 (默认 true)
- `duration` - 动画持续时间(毫秒)
- `easing` - 缓动函数
- `essential` - 是否为必要动画(不受 prefers-reduced-motion 影响)
- `curve` - 缩放曲线值
- `speed` - 平均速度
- `screenSpeed` - 屏幕速度
- `minZoom` - 最小缩放级别
- `maxDuration` - 最大持续时间
- `offset` - 目标中心偏移量
- `preloadOnly` - 仅预加载瓦片

### 示例

```javascript
map.flyTo({
  center: [800000, 600000],
  zoom: 14,
  duration: 5000,
  essential: true,
  curve: 1.42
});
```

## CameraOptions - 相机选项

控制相机位置、缩放、旋转和俯仰的选项。

### 属性

- `center` - 中心点坐标
- `zoom` - 缩放级别
- `bearing` - 旋转角度
- `pitch` - 俯仰角
- `padding` - 填充选项
- `around` - 变换原点
- `retainPadding` - 是否保留填充 (默认 true)

### 示例

```javascript
// 注意：此处需改为你的后台服务地址
dmapgl.serviceUrl = 'http://172.26.64.84/api';
var map = new dmapgl.Map({
  container: 'map',
  style: dmapgl.serviceUrl + '/map/vector/standard/styles/style.json',
  center: [800000, 600000],
  pitch: 60,
  bearing: -60,
  zoom: 11
});
```

## CustomLayerInterface - 自定义图层接口

自定义样式图层的接口规范。

### 属性

- `id` - 唯一图层ID
- `type` - 类型 (必须为 "custom")
- `renderingMode` - 渲染模式 ("2d" 或 "3d")
- `wrapTileId` - 是否包装瓦片ID

### 必需方法

- `render(gl, matrix)` - 渲染方法
- `prerender(gl, matrix)` - 预渲染方法(可选)
- `onAdd(map, gl)` - 添加时调用(可选)
- `onRemove()` - 移除时调用(可选)

## 工具函数

### LngLat转换

```javascript
// 转换为LngLat对象
const coord = dmapgl.LngLat.convert([800000, 600000]);
console.log(coord.lng, coord.lat);
```

### LngLatBounds转换

```javascript
// 转换为边界对象
const bounds = dmapgl.LngLatBounds.convert([
  [716638.24, 548483.59],
  [894455.08, 728066.47]
]);
```

### Point转换

```javascript
// 创建Point对象
const point = new dmapgl.Point(100, 200);
```

## 浏览器检测

### Browser对象

```javascript
// 检测浏览器特性
console.log('支持WebGL:', dmapgl.browser.webglSupported);
console.log('支持触控:', dmapgl.browser.touchSupport);
console.log('设备像素比:', dmapgl.browser.devicePixelRatio);
```

## 实用示例

### 完整的初始化检查

```javascript
function initializeMap() {
  // 1. 检查浏览器支持
  if (!dmapgl.supported()) {
    showError('您的浏览器不支持DMap GL');
    return;
  }
  
  // 2. 指定后台服务地址
  dmapgl.serviceUrl = 'http://172.26.64.84/api';

  
  // 3. 创建地图
  var map = new dmapgl.Map({
    container: 'map',
    style: dmapgl.serviceUrl + '/map/vector/standard/styles/style.json',
    center: [800000, 600000],
    zoom: 11
  });
  
  return map;
}
```

### 清除缓存刷新

```javascript
function refreshMap() {
  dmapgl.clearData((error) => {
    if (error) {
      console.error('清除缓存失败:', error);
      return;
    }
    
    // 重新加载页面
    window.location.reload();
  });
}
```

### 环境检测

```javascript
function detectEnvironment() {
  const env = {
    supported: dmapgl.supported(),
    version: dmapgl.version,
    webgl: dmapgl.browser.webglSupported,
    touch: dmapgl.browser.touchSupport,
    pixelRatio: dmapgl.browser.devicePixelRatio,
    workers: dmapgl.workerCount
  };
  
  console.log('环境信息:', env);
  return env;
}

detectEnvironment();
```

## 注意事项

1. **后台服务地址**: 请确保后台服务地址正确
2. **supported**: 在创建Map前检查浏览器支持
3. **clearData**: 清除操作不可逆
4. **RTL插件**: 仅在需要时加载
5. **Worker数量**: 根据设备性能调整
6. **版本兼容**: 注意API版本变化


path: references/api/geography.md
content: 

# Geography - 地理和几何

用于处理地理坐标、边界框和屏幕位置的类。

## LngLat

表示地理坐标(经度,纬度)。

### 构造函数

```javascript
new dmapgl.LngLat(lng, lat)
```

### 属性

- `lng` - 经度 (-180 到 180)
- `lat` - 纬度 (-90 到 90)

### 静态方法

- `convert(input)` - 转换为LngLat对象

### 实例方法

- `wrap()` - 包装经度到 -180~180 范围
- `toArray()` - 转换为数组 [lng, lat]
- `toString()` - 转换为字符串
- `distanceTo(lnglat)` - 计算到另一点的距离(米)
- `toBounds(radius)` - 转换为边界框

### 示例

```javascript
const coord = new dmapgl.LngLat(800000, 600000);
console.log(coord.lng, coord.lat);

// 转换
const coord2 = dmapgl.LngLat.convert([116.4, 39.9]);

// 距离
const distance = coord.distanceTo(coord2);
console.log(`距离: ${distance} 米`);
```

## LngLatBounds

表示地理边界框(西南角和东北角)。

### 构造函数

```javascript
new dmapgl.LngLatBounds(sw, ne)
```

### 静态方法

- `convert(input)` - 转换为LngLatBounds对象

### 实例方法

- `setNorthEast(ne)` - 设置东北角
- `setSouthWest(sw)` - 设置西南角
- `extend(obj)` - 扩展边界包含对象
- `getCenter()` - 获取中心点
- `getSouthWest()` - 获取西南角
- `getNorthEast()` - 获取东北角
- `getNorthWest()` - 获取西北角
- `getSouthEast()` - 获取东南角
- `getWest()` - 获取西边界
- `getSouth()` - 获取南边界
- `getEast()` - 获取东边界
- `getNorth()` - 获取北边界
- `toArray()` - 转换为数组 [[sw], [ne]]
- `toString()` - 转换为字符串
- `isEmpty()` - 检查是否为空
- `contains(lnglat)` - 检查是否包含坐标
- `intersects(bounds)` - 检查是否与另一边界相交

### 示例

```javascript
// 创建边界
const bounds = new dmapgl.LngLatBounds(
  [750000, 560000],  // 西南角
  [850000, 650000]   // 东北角
);

// 从地图获取
const mapBounds = map.getBounds();

// 检查是否包含
if (mapBounds.contains([116.4, 39.9])) {
  console.log('坐标在可视区域内');
}

// 扩展边界
bounds.extend([116.8, 40.2]);

// 适配视图
map.fitBounds(bounds);
```

## Point

表示二维点(x, y)。

### 构造函数

```javascript
new dmapgl.Point(x, y)
```

### 属性

- `x` - X坐标
- `y` - Y坐标

### 实例方法

- `clone()` - 克隆点
- `add(point)` - 加法
- `sub(point)` - 减法
- `mult(k)` - 乘法
- `div(k)` - 除法
- `rotate(angle)` - 旋转
- `matMult(m)` - 矩阵乘法
- `unit()` - 单位化
- `perp()` - 垂直向量
- `round()` - 四舍五入
- `mag()` - 长度
- `dist(point)` - 距离
- `angleWith(point)` - 夹角
- `angle()` - 角度

### 示例

```javascript
const p1 = new dmapgl.Point(100, 200);
const p2 = new dmapgl.Point(150, 250);

const distance = p1.dist(p2);
console.log(`距离: ${distance}`);
```

## MercatorCoordinate

墨卡托坐标,用于3D投影。

### 构造函数

```javascript
new dmapgl.MercatorCoordinate(x, y, z)
```

### 静态方法

- `fromLngLat(lngLat, altitude)` - 从经纬度创建

### 属性

- `x` - X坐标
- `y` - Y坐标
- `z` - Z坐标(高度)

### 实例方法

- `toLngLat()` - 转换为经纬度
- `toAltitude()` - 转换为海拔高度
- `meterInMercatorCoordinateUnits()` - 每米的墨卡托单位

### 示例

```javascript
// 从经纬度创建
const merc = dmapgl.MercatorCoordinate.fromLngLat(
  [800000, 600000],
  100 // 海拔高度(米)
);

// 转换回经纬度
const lngLat = merc.toLngLat();
```

## Pixel

表示屏幕像素坐标。

### 类型定义

```typescript
type Pixel = [number, number]; // [x, y]
```

## PointLike

Point或数字数组的灵活类型。

### 类型定义

```typescript
type PointLike = Point | [number, number];
```

### 示例

```javascript
const p1 = new dmapgl.Point(400, 525); // PointLike
const p2 = [400, 525]; // PointLike
```

## LngLatLike

LngLat或数组的灵活类型。

### 类型定义

```typescript
type LngLatLike = LngLat | 
                  {lng: number, lat: number} | 
                  {lon: number, lat: number} | 
                  [number, number];
```

### 示例

```javascript
const v1 = new dmapgl.LngLat(-122.42, 37.77);
const v2 = [-122.42, 37.77];
const v3 = {lon: -122.42, lat: 37.77};
```

## LngLatBoundsLike

LngLatBounds或数组的灵活类型。

### 类型定义

```typescript
type LngLatBoundsLike = LngLatBounds | 
                        [LngLatLike, LngLatLike] | 
                        [number, number, number, number];
```

### 示例

```javascript
const v1 = new dmapgl.LngLatBounds(
  new dmapgl.LngLat(-73.98, 40.76),
  new dmapgl.LngLat(-73.93, 40.80)
);
const v2 = [[-73.98, 40.76], [-73.93, 40.80]];
const v3 = [-73.98, 40.76, -73.93, 40.80];
```

## 坐标转换

### 地理坐标转像素

```javascript
const point = map.project([800000, 600000]);
console.log('像素坐标:', point.x, point.y);
```

### 像素转地理坐标

```javascript
const lngLat = map.unproject(new dmapgl.Point(100, 200));
console.log('地理坐标:', lngLat.lng, lngLat.lat);
```

## 实用示例

### 计算两点距离

```javascript
function calculateDistance(coord1, coord2) {
  const lngLat1 = dmapgl.LngLat.convert(coord1);
  const lngLat2 = dmapgl.LngLat.convert(coord2);
  
  return lngLat1.distanceTo(lngLat2);
}

const distance = calculateDistance(
  [800000, 600000],
  [116.407, 39.904]
);

console.log(`距离: ${(distance / 1000).toFixed(2)} km`);
```

### 检查点在边界内

```javascript
function isPointInBounds(point, bounds) {
  const lngLat = dmapgl.LngLat.convert(point);
  const bbox = dmapgl.LngLatBounds.convert(bounds);
  
  return bbox.contains(lngLat);
}

if (isPointInBounds([116.4, 39.9], [[750000, 560000], [850000, 650000]])) {
  console.log('点在边界内');
}
```

### 获取边界中心

```javascript
const bounds = map.getBounds();
const center = bounds.getCenter();

console.log('边界中心:', center.lng, center.lat);
```

### 扩展边界包含所有点

```javascript
const points = [
  [800000, 600000],
  [116.407, 39.904],
  [820000, 620000]
];

let bounds = new dmapgl.LngLatBounds();

points.forEach(point => {
  bounds.extend(point);
});

map.fitBounds(bounds, { padding: 50 });
```

## 注意事项

1. **坐标顺序**: DMap GL使用 [lng, lat] 顺序,而非 [lat, lng]
2. **有效范围**: 经度 -180~180,纬度 -90~90
3. **距离计算**: distanceTo 返回米为单位
4. **边界有效性**: 确保西南角的经纬度小于东北角
5. **投影限制**: 墨卡托投影在高纬度地区有变形


path: references/api/handlers.md
content: 

# Handlers - 交互处理器

Handler控制地图的用户交互行为，如缩放、平移、旋转等。

## BoxZoomHandler - 框选缩放

通过拖拽矩形框进行缩放。

### 启用/禁用

```javascript
// 禁用框选缩放
map.boxZoom.disable();

// 启用框选缩放
map.boxZoom.enable();

// 检查是否启用
if (map.boxZoom.isEnabled()) {
  console.log('框选缩放已启用');
}
```

### 使用方法

按住Shift键并拖拽鼠标绘制矩形框来缩放。

## ScrollZoomHandler - 滚轮缩放

通过鼠标滚轮进行缩放。

### 启用/禁用

```javascript
// 禁用滚轮缩放
map.scrollZoom.disable();

// 启用滚轮缩放
map.scrollZoom.enable();

// 检查状态
if (map.scrollZoom.isEnabled()) {
  console.log('滚轮缩放已启用');
}
```

### 配置选项

```javascript
// 启用并配置
map.scrollZoom.enable({
  around: 'center'  // 'center' 或 null
});

// 设置缩放速度
map.scrollZoom.setWheelZoomRate(1);  // 默认值
```

### 协作手势

```javascript
var map = new dmapgl.Map({
  container: 'map',
  style: style,
  cooperativeGestures: true  // 需要Ctrl+滚轮才能缩放
});
```

## DragPanHandler - 拖拽平移

通过拖拽移动地图。

### 启用/禁用

```javascript
// 禁用拖拽平移
map.dragPan.disable();

// 启用拖拽平移
map.dragPan.enable();

// 检查状态
if (map.dragPan.isEnabled()) {
  console.log('拖拽平移已启用');
}
```

### 配置选项

```javascript
// 启用并配置
map.dragPan.enable({
  linearity: 0.3,      // 线性度(0-1)
  easing: (t) => t,    // 缓动函数
  deceleration: 0.8,   // 减速度
  maxSpeed: 1400       // 最大速度
});
```

## DragRotateHandler - 拖拽旋转

通过右键拖拽旋转地图。

### 启用/禁用

```javascript
// 禁用拖拽旋转
map.dragRotate.disable();

// 启用拖拽旋转
map.dragRotate.enable();

// 检查状态
if (map.dragRotate.isEnabled()) {
  console.log('拖拽旋转已启用');
}
```

### 配合倾斜

```javascript
// 右键拖拽同时旋转和倾斜
map.dragRotate.enable();

// 仅旋转，不倾斜
map.touchZoomRotate.disableRotation();
```

## KeyboardHandler - 键盘交互

通过键盘控制地图。

### 启用/禁用

```javascript
// 禁用键盘交互
map.keyboard.disable();

// 启用键盘交互
map.keyboard.enable();

// 检查状态
if (map.keyboard.isEnabled()) {
  console.log('键盘交互已启用');
}
```

### 快捷键

- **+**: 放大
- **-**: 缩小
- **←↑→↓**: 平移
- **Shift + ←↑→↓**: 快速平移
- **Shift + ←→**: 旋转
- **Shift + ↑↓**: 倾斜

## DoubleClickZoomHandler - 双击缩放

双击放大，Shift+双击缩小。

### 启用/禁用

```javascript
// 禁用双击缩放
map.doubleClickZoom.disable();

// 启用双击缩放
map.doubleClickZoom.enable();

// 检查状态
if (map.doubleClickZoom.isEnabled()) {
  console.log('双击缩放已启用');
}
```

## TouchZoomRotateHandler - 触摸缩放旋转

通过双指触摸进行缩放和旋转。

### 启用/禁用

```javascript
// 禁用触摸缩放旋转
map.touchZoomRotate.disable();

// 启用
map.touchZoomRotate.enable();

// 仅禁用旋转
map.touchZoomRotate.disableRotation();

// 检查状态
if (map.touchZoomRotate.isEnabled()) {
  console.log('触摸缩放旋转已启用');
}
```

## TwoFingersTouchPitchHandler - 双指倾斜

通过双指触摸进行倾斜。

### 启用/禁用

```javascript
// 禁用双指倾斜
map.twoFingersTouchPitch.disable();

// 启用
map.twoFingersTouchPitch.enable();
```

## TwoFingersTouchZoomRotateHandler - 双指缩放旋转

通过双指触摸进行缩放和旋转（移动端）。

### 启用/禁用

```javascript
// 禁用
map.twoFingersTouchZoomRotate.disable();

// 启用
map.twoFingersTouchZoomRotate.enable();

// 仅禁用旋转
map.twoFingersTouchZoomRotate.disableRotation();
```

## 批量控制交互

### 禁用所有交互

```javascript
map.dragPan.disable();
map.dragRotate.disable();
map.scrollZoom.disable();
map.touchZoomRotate.disable();
map.keyboard.disable();
map.doubleClickZoom.disable();
map.boxZoom.disable();
```

### 启用所有交互

```javascript
map.dragPan.enable();
map.dragRotate.enable();
map.scrollZoom.enable();
map.touchZoomRotate.enable();
map.keyboard.enable();
map.doubleClickZoom.enable();
map.boxZoom.enable();
```

### 初始化时配置

```javascript
// 注意：此处需改为你的后台服务地址
dmapgl.serviceUrl = 'http://172.26.64.84/api';
var map = new dmapgl.Map({
  container: 'map',
  style: style,
  center: [800000, 600000],
  zoom: 11,
  
  // 禁用特定交互
  dragRotate: false,      // 禁用旋转
  touchZoomRotate: false, // 禁用触摸旋转
  doubleClickZoom: false, // 禁用双击缩放
  boxZoom: false,         // 禁用框选缩放
  
  // 或全部禁用
  interactive: false      // 禁用所有交互
});
```

## 实用示例

### 只读模式

```javascript
function setReadOnlyMode(map) {
  map.dragPan.disable();
  map.dragRotate.disable();
  map.scrollZoom.disable();
  map.touchZoomRotate.disable();
  map.keyboard.disable();
  map.doubleClickZoom.disable();
  map.boxZoom.disable();
}

setReadOnlyMode(map);
```

### 移动端优化

```javascript
const isMobile = window.innerWidth < 768;
// 注意：此处需改为你的后台服务地址
dmapgl.serviceUrl = 'http://172.26.64.84/api';
var map = new dmapgl.Map({
  container: 'map',
  style: style,
  center: [800000, 600000],
  zoom: 11,
  
  // 移动端禁用某些交互
  dragRotate: !isMobile,
  keyboard: !isMobile,
  boxZoom: !isMobile,
  
  // 协作手势防止误触
  cooperativeGestures: isMobile
});
```

### 条件启用交互

```javascript
let editMode = false;

function toggleEditMode() {
  editMode = !editMode;
  
  if (editMode) {
    // 编辑模式：启用所有交互
    map.dragPan.enable();
    map.dragRotate.enable();
    map.scrollZoom.enable();
  } else {
    // 查看模式：禁用部分交互
    map.dragRotate.disable();
    map.scrollZoom.disable();
  }
}

document.getElementById('edit-toggle').onclick = toggleEditMode;
```

## 注意事项

1. **性能**: 禁用的handler不会监听事件，提升性能
2. **用户体验**: 根据场景选择合适的交互
3. **移动端**: 考虑触摸设备的特殊性
4. **无障碍**: 保留键盘交互支持无障碍访问
5. **冲突**: 多个handler可能产生冲突
6. **状态管理**: 跟踪handler的启用状态


path: references/api/events.md
content: 

# Events - 事件系统

DMap GL的事件系统用于响应地图交互和状态变化。

## Evented

事件基础类,提供事件绑定和解绑功能。

### 实例方法

- `on(type, handler)` - 绑定事件监听器
- `off(type, handler)` - 解绑事件监听器
- `once(type, handler)` - 绑定一次性事件监听器
- `fire(type, data)` - 触发事件

## MapMouseEvent

鼠标事件对象。

### 属性

- `type` - 事件类型
- `target` - 目标对象(Map)
- `originalEvent` - 原始DOM事件
- `point` - 像素坐标 Point {x, y}
- `lngLat` - 地理坐标 LngLat {lng, lat}
- `features` - 要素数组(仅图层事件)

### 实例方法

- `preventDefault()` - 阻止默认行为

## MapTouchEvent

触摸事件对象。

### 属性

- `type` - 事件类型
- `target` - 目标对象
- `originalEvent` - 原始触摸事件
- `point` - 主要触摸点像素坐标
- `points` - 触摸点数组
- `lngLat` - 主要触摸点地理坐标
- `lngLats` - 地理坐标数组
- `features` - 要素数组

### 实例方法

- `preventDefault()` - 阻止默认行为

## MapWheelEvent

滚轮事件对象。

### 属性

- `type` - 事件类型 ("wheel")
- `target` - 目标对象(Map)
- `originalEvent` - 原始 WheelEvent

### 实例方法

- `preventDefault()` - 阻止默认行为

## MapBoxZoomEvent

框选缩放事件对象。

### 属性

- `type` - 事件类型 ("boxzoomstart" | "boxzoomend" | "boxzoomcancel")
- `target` - 目标对象(Map)
- `originalEvent` - 原始 DOM 事件

### 示例

```javascript
map.on('boxzoomend', (e) => {
  console.log('框选边界:', e.boxZoomBounds);
});
```

## DataEvent

数据加载事件对象。

### 属性

- `type` - 事件类型
- `target` - 目标对象
- `dataType` - 数据类型 ('source' | 'style')
- `isSourceLoaded` - 源是否加载完成
- `sourceId` - 源ID(仅source类型)
- `source` - 源配置对象(仅source类型)
- `sourceDataType` - 源数据类型 ('metadata' | 'content' | 'visibility' | 'error')
- `coord` - 瓦片坐标(仅瓦片加载事件)
- `tile` - 瓦片对象(仅瓦片加载事件)

## 地图事件类型

### 鼠标事件

- `click` - 点击
- `dblclick` - 双击
- `mousedown` - 鼠标按下
- `mouseup` - 鼠标释放
- `mousemove` - 鼠标移动
- `mouseover` - 鼠标进入
- `mouseout` - 鼠标离开
- `contextmenu` - 右键菜单

### 触摸事件

- `touchstart` - 触摸开始
- `touchend` - 触摸结束
- `touchmove` - 触摸移动
- `touchcancel` - 触摸取消

### 视图事件

- `movestart` - 移动开始
- `move` - 移动中
- `moveend` - 移动结束
- `zoomstart` - 缩放开始
- `zoom` - 缩放中
- `zoomend` - 缩放结束
- `rotatestart` - 旋转开始
- `rotate` - 旋转中
- `rotateend` - 旋转结束
- `pitchstart` - 俯仰开始
- `pitch` - 俯仰中
- `pitchend` - 俯仰结束

### 加载事件

- `load` - 地图加载完成
- `render` - 渲染帧
- `idle` - 空闲状态
- `error` - 错误

### 数据事件

- `dataloading` - 数据加载中
- `data` - 数据加载
- `sourcedataloading` - 源数据加载中
- `sourcedata` - 源数据加载
- `styledataloading` - 样式数据加载中
- `styledata` - 样式数据加载

### 图层事件

- `layer.add` - 图层添加
- `layer.remove` - 图层移除

## 使用示例

```javascript
// 绑定事件
map.on('click', (e) => {
  console.log('点击位置:', e.lngLat);
});

// 图层事件
map.on('click', 'my-layer', (e) => {
  console.log('要素:', e.features[0]);
});

// 一次性事件
map.once('load', () => {
  console.log('地图已加载');
});

// 解绑事件
const handler = (e) => console.log(e);
map.on('move', handler);
map.off('move', handler);

```

详见 [事件文档](../events.md)


path: references/style/sprite.md
content: 

# Sprite - 图标集

Sprite 提供用于渲染地图符号的小图标集合。

## 配置

### 基本配置

```javascript
{
  "sprite": "zyzx://vector_2025/standard/sprites/sprite"
}
```

### 自定义 Sprite

```javascript
{
  "sprite": "https://example.com/sprites/mysprite"
}
```

这会加载:
- `https://example.com/sprites/mysprite.json` - 元数据
- `https://example.com/sprites/mysprite.png` - 图片
- `https://example.com/sprites/[REDACTED_SECRET]` - 高分辨率图片

## 使用 Sprite 图标

### 在图层中使用

```javascript
map.addLayer({
  id: 'icons',
  type: 'symbol',
  source: 'places',
  layout: {
    'icon-image': 'restaurant-15',  // sprite中的图标ID
    'icon-size': 1
  }
});
```

### 添加自定义图标

```javascript
// 从图片添加
const image = new Image(32, 32);
image.src = 'custom-icon.png';
image.onload = () => {
  map.addImage('custom-icon', image);
};

// 使用
map.addLayer({
  id: 'custom-icons',
  type: 'symbol',
  source: 'points',
  layout: {
    'icon-image': 'custom-icon'
  }
});
```

### 动态生成图标

```javascript
// 创建Canvas图标
const canvas = document.createElement('canvas');
canvas.width = 64;
canvas.height = 64;
const ctx = canvas.getContext('2d');

// 绘制圆形
ctx.beginPath();
ctx.arc(32, 32, 28, 0, 2 * Math.PI);
ctx.fillStyle = '#3b82f6';
ctx.fill();
ctx.strokeStyle = '#fff';
ctx.lineWidth = 4;
ctx.stroke();

// 添加到地图
map.addImage('circle-icon', canvas);
```

## Sprite 元数据格式

```json
{
  "restaurant-15": {
    "width": 32,
    "height": 32,
    "x": 0,
    "y": 0,
    "pixelRatio": 1,
    "visible": true
  },
  "cafe-15": {
    "width": 32,
    "height": 32,
    "x": 32,
    "y": 0,
    "pixelRatio": 1,
    "visible": true
  }
}
```

## 注意事项

1. **性能**: 合并多个小图标到一个sprite减少请求
2. **分辨率**: 提供@2x版本支持高分辨率屏幕
3. **大小**: 单个sprite不宜过大，建议拆分为多个
4. **缓存**: sprite会被浏览器缓存
5. **更新**: 修改sprite后需要清除缓存


path: references/style/sources.md
content: 

# 数据源类型 (Sources)

样式规范定义的数据源类型。

## vector - 矢量瓦片

矢量格式的马赛克瓦片数据。

### 配置属性

```javascript
{
  "type": "vector",
  
  // 瓦片URL模板
  "tiles": [
    "https://example.com/{z}/{x}/{y}.pbf"
  ],
  
  // 最小缩放级别
  "minzoom": 0,
  
  // 最大缩放级别
  "maxzoom": 14,
  
  // 瓦片尺寸(512或256)
  "tileSize": 512,
  
  // 方案(xyz或tms)
  "scheme": "xyz",
  
  // 地理边界
  "bounds": [-180, -85.051129, 180, 85.051129],
  
  // 版权信息
  "attribution": "© DmapGL"
}
```

## raster - 栅格瓦片

图片格式的瓦片数据。

### 配置属性

```javascript
{
  "type": "raster",
  
  // 瓦片URL模板
  "tiles": [
    "https://example.com/{z}/{x}/{y}.png"
  ],
  
  // 瓦片尺寸(256或512)
  "tileSize": 256,
  
  // 最小/最大缩放
  "minzoom": 0,
  "maxzoom": 22,
  
  // 方案
  "scheme": "xyz",
  
  // 边界
  "bounds": [-180, -85.051129, 180, 85.051129],
  
  // 版权
  "attribution": "© Satellite Provider"
}
```

### WMS服务示例

```javascript
{
  "type": "raster",
  "tiles": [
    "https://img.nj.gov/imagerywms/Natural2015?" +
    "bbox={bbox-epsg-3857}" +
    "&format=image/png" +
    "&service=WMS" +
    "&version=1.1.1" +
    "&request=GetMap" +
    "&srs=EPSG:3857" +
    "&transparent=true" +
    "&width=256" +
    "&height=256" +
    "&layers=Natural2015"
  ],
  "tileSize": 256
}
```

## raster-dem - 栅格DEM

用于地形渲染的高程数据。

### 配置属性

```javascript
{
  "type": "raster-dem",
  
  // 瓦片URL
  "tiles": [
    "https://example.com/terrain/{z}/{x}/{y}.png"
  ],
  
  // 瓦片尺寸(通常512)
  "tileSize": 512,
  
  // 编码方式(mapbox或terrarium)
  "encoding": "terrarium",
  
  // 最小/最大缩放
  "minzoom": 0,
  "maxzoom": 14
}
```

## geojson - GeoJSON数据

GeoJSON格式的矢量数据。

### 配置属性

```javascript
{
  "type": "geojson",
  
  // GeoJSON数据或URL
  "data": {
    "type": "FeatureCollection",
    "features": []
  },
  
  // 或URL
  // "data": "https://example.com/data.geojson",
  
  // 最大缩放
  "maxzoom": 14,
  
  // 瓦片缓冲
  "buffer": 128,
  
  // 容差(简化几何)
  "tolerance": 0.375,
  
  // 启用聚类
  "cluster": false,
  "clusterMaxZoom": 14,
  "clusterRadius": 50,
  
  // 行度量(用于lineMetrics)
  "lineMetrics": false,
  
  // 生成ID
  "generateId": false,
  
  // 促进ID
  "promoteId": null
}
```

### 聚类配置

```javascript
{
  "type": "geojson",
  "data": pointData,
  "cluster": true,
  "clusterMaxZoom": 14,
  "clusterRadius": 50,
  "clusterProperties": {
    "sum": ["+", ["get", "value"]],
    "max": ["max", ["get", "value"]]
  }
}
```

## image - 单张图片

单张地理参考图片。

### 配置属性

```javascript
{
  "type": "image",
  
  // 图片URL
  "url": "https://example.com/overlay.png",
  
  // 四角坐标 [[左上], [右上], [右下], [左下]]
  "coordinates": [
    [-76.54, 39.18],  // 左上
    [-76.52, 39.18],  // 右上
    [-76.52, 39.17],  // 右下
    [-76.54, 39.17]   // 左下
  ]
}
```

## video - 视频

地理参考视频。

### 配置属性

```javascript
{
  "type": "video",
  
  // 视频URL数组(多种格式)
  "urls": [
    "https://example.com/video.mp4",
    "https://example.com/video.webm"
  ],
  
  // 四角坐标
  "coordinates": [
    [-76.54, 39.18],
    [-76.52, 39.18],
    [-76.52, 39.17],
    [-76.54, 39.17]
  ]
}
```

## canvas - Canvas

HTML Canvas元素作为数据源。

### 配置属性

```javascript
{
  "type": "canvas",
  
  // Canvas元素ID或引用
  "canvas": "my-canvas-id",
  
  // 是否动画
  "animate": true,
  
  // 四角坐标
  "coordinates": [
    [-76.54, 39.18],
    [-76.52, 39.18],
    [-76.52, 39.17],
    [-76.54, 39.17]
  ]
}
```

## 使用示例

### 添加多个数据源

```javascript
// 矢量瓦片
map.addSource('custom-vector', {
    type: 'vector',
    tiles: [
        'https://example.com/tiles/{z}/{x}/{y}.pbf'
    ],
    minzoom: 7,
    maxzoom: 14
});

// 栅格瓦片
map.addSource('custom-raster', {
    type: 'raster',
    tiles: [
        'https://example.com/tiles/{z}/{x}/{y}.png'
    ],
    tileSize: 512
});


// GeoJSON
map.addSource('points', {
  type: 'geojson',
  data: geoJsonData,
  cluster: true
});

// DEM地形
map.addSource('terrain', {
  type: 'raster-dem',
  tiles: [
     dmapgl.serviceUrl + '/map/img?year=&type=DEM&z={z}&x={x}&y={y}'
  ],
  tileSize: 512
});

// 图片叠加
map.addSource('overlay', {
  type: 'image',
  url: 'overlay.png',
  coordinates: [
    [-76.54, 39.18],
    [-76.52, 39.18],
    [-76.52, 39.17],
    [-76.54, 39.17]
  ]
});
```

### 动态更新数据源

```javascript
// 更新GeoJSON
const source = map.getSource('points');
source.setData(newGeoJsonData);

// 更新栅格瓦片URL
const raster = map.getSource('satellite');
raster.setTiles(['https://new-tiles.example.com/{z}/{x}/{y}.png']);

// 更新图片
const image = map.getSource('overlay');
image.updateImage({
  url: 'new-image.png',
  coordinates: [...]
});
```

## 注意事项

1. **CORS**: 外部资源需要支持跨域访问
2. **性能**: 大数据集使用矢量瓦片而非GeoJSON
3. **缓存**: 瓦片会自动缓存
4. **TileJSON**: 优先使用TileJSON简化配置
5. ** attribution**: 始终提供正确的版权信息
6. **瓦片尺寸**: 选择合适的tileSize(256或512)


path: references/style/root.md
content: 

# Root - 根属性

样式文件的根级别属性，定义地图的基本配置。

## 必需属性

### version

样式规范版本（必须为8）。

```javascript
{
  "version": 8
}
```

## 可选属性

### name

样式的名称。

```javascript
{
  "version": 8,
  "name": "My Custom Style"
}
```

### metadata

元数据，用于存储自定义信息。

```javascript
{
  "version": 8,
  "metadata": {
    "mapcenter": [801292.5499279505, 606918.7969073517],
    "mapbounds": [716638.2414098255, 548483.5939021005, 894455.0756895209, 728066.4667000007],
    "indexbounds": [-4823200.0, -528738.6842773687, 1888538.6842773688, 6183000.0],
    "mapscale": 0.002828908127455374,
    "topscale": 2.018358341600529e-8,
    "epsgcode": 0
  }
}
```

### center

地图初始中心点。

```javascript
{
  "version": 8,
  "center": [800000, 600000]  // [x, y] 地方平面坐标
}
```

### zoom

初始缩放级别。

```javascript
{
  "version": 8,
  "zoom": 11
}
```

### bearing

初始旋转角度（逆时针，单位：度）。

```javascript
{
  "version": 8,
  "bearing": 0
}
```

### pitch

初始俯仰角（0-85度）。

```javascript
{
  "version": 8,
  "pitch": 0
}
```

### sources

数据源定义对象。

```javascript
{
  "version": 8,
  "sources": {
    "矢量切片": {
      "tiles": ["zyzx://vector_2025/standard/tiles/{z}/{x}/{y}.mvt"],
      "type": "vector"
    },
    "satellite": {
      "type": "raster",
      "tiles": ["zyzx://img?year=&type=Sate&z={z}&x={x}&y={y}'],
      'tileSize': 512
    }
  }
}
```

详见 [Sources](sources.md)

### layers

图层定义数组。

```javascript
{
  "version": 8,
  "sources": { /* ... */ },
  "layers": [
    {
      "id": "background",
      "type": "background",
      "paint": {
        "background-color": "#f0f0f0"
      }
    },
    {
      "id": "roads",
      "type": "line",
      "source": "satellite",
      "source-layer": "road",
      "paint": {
        "line-color": "#3b82f6",
        "line-width": 2
      }
    }
  ]
}
```

详见 [Layers](layers.md)

### sprite

图标集URL。

```javascript
{
  "version": 8,
  "sprite": "zyzx://vector_2025/standard/sprites/sprite"
}
```

详见 [Sprite](sprite.md)

### glyphs

字体URL模板。

```javascript
{
  "version": 8,
  "glyphs": "zyzx://vector_2025/standard/fonts/{fontstack}/{range}.pbf"
}
```

详见 [Glyphs](glyphs.md)

### transition

全局过渡动画配置。

```javascript
{
  "version": 8,
  "transition": {
    "duration": 300,
    "delay": 0
  }
}
```

详见 [Transition](transition.md)

### light

全局光照配置。

```javascript
{
  "version": 8,
  "light": {
    "anchor": "viewport",
    "position": [1, 90, 80],
    "intensity": 0.5
  }
}
```

详见 [Light](light.md)

### terrain

地形配置。

```javascript
{
  "version": 8,
  "terrain": {
    "source": "dmap-dem",
    "exaggeration": 1.5
  }
}
```

详见 [Terrain](terrain.md)

### fog

雾效配置。

```javascript
{
  "version": 8,
  "fog": {
    "range": [0.5, 10],
    "color": "#ffffff",
    "horizon-blend": 0.1
  }
}
```

详见 [Fog](fog.md)


## 完整示例

```javascript
{
  "version": 8,
  "name": "DMap Custom Style",
  "metadata": {
  },
  "center": [800000, 600000],
  "zoom": 11,
  "bearing": 0,
  "pitch": 0,
  "sources": {
    "矢量切片": {
      "type": "vector",
      "tiles": ["zyzx://vector_2025/standard/tiles/{z}/{x}/{y}.mvt"]
    }
  },
  "sprite": "zyzx://vector_2025/standard/sprites/sprite",
  "glyphs": "zyzx://vector_2025/standard/fonts/{fontstack}/{range}.pbf",
  "layers": [
    {
      "id": "background",
      "type": "background",
      "paint": {
        "background-color": "#f0f0f0"
      }
    }
  ],
  "transition": {
    "duration": 300
  },
  "light": {
    "anchor": "viewport",
    "position": [1, 90, 80],
    "intensity": 0.5
  }
}
```

## 注意事项

1. **version**: 必须为8
2. **sources和layers**: 通常一起使用
3. **顺序**: layers数组的顺序决定渲染顺序
4. **引用**: layer的source必须在sources中定义
5. **性能**: 过多的layers影响性能
6. **验证**: 使用样式验证工具检查格式


path: references/style/glyphs.md
content: 

# Glyphs - 字体

Glyphs 提供文本标签渲染所需的字体系列。

## 配置

### 基本配置

```javascript
{
  "glyphs": "zyzx://vector_2025/standard/fonts/{fontstack}/{range}.pbf"
}
```

### URL模板

URL模板包含两个占位符:
- `{fontstack}` - 字体名称，如 "Open Sans Regular"
- `{range}` - Unicode范围，如 "0-255"

### 自定义字体服务

```javascript
{
  "glyphs": "https://example.com/fonts/{fontstack}/{range}.pbf"
}
```

## 使用字体

### 在图层中指定字体

```javascript
map.addLayer({
  id: 'labels',
  type: 'symbol',
  source: 'places',
  layout: {
    'text-field': ['get', 'name'],
    'text-font': ['Open Sans Regular', 'Arial Unicode MS Regular'],
    'text-size': 14
  }
});
```

### 字体回退

```javascript
'text-font': [
  'Noto Sans SC Regular',  // 首选
  'Noto Sans Regular',     // 备选1
  'Arial Unicode MS Regular'  // 备选2
]
```

## 常用字体

### 英文字体

- Open Sans Regular
- Open Sans Bold
- Open Sans Italic
- Arial Unicode MS Regular

### 中文字体

- Noto Sans SC Regular (简体中文)
- Noto Sans SC Bold
- Noto Sans TC Regular (繁体中文)
- PingFang SC Regular

### 多语言支持

```javascript
'text-font': [
  'Noto Sans SC Regular',  // 中文
  'Noto Sans JP Regular',  // 日文
  'Noto Sans KR Regular',  // 韩文
  'Open Sans Regular'      // 拉丁文
]
```

## 本地字体

### 使用系统字体

```javascript
var map = new dmapgl.Map({
  container: 'map',
  style: style,
  localIdeographFontFamily: 'sans-serif'  // CJK字符使用本地字体
});
```

### 禁用本地字体

```javascript
var map = new dmapgl.Map({
  container: 'map',
  style: style,
  localIdeographFontFamily: false  // 使用服务器字体
});
```

## 字体格式

Glyphs 使用 Signed Distance Field (SDF) 格式的 PBF 文件:

- **PBF**: Protocol Buffers 格式
- **SDF**: 支持任意缩放不失真
- **分块**: 按Unicode范围分块加载

## Unicode范围

常见的Unicode范围块:

- `0-255`: 基本拉丁字符
- `256-511`: 拉丁扩展
- `19968-40959`: CJK统一表意文字
- `12352-12447`: 平假名
- `12448-12543`: 片假名
- `44032-55215`: 韩文音节

## 注意事项

1. **性能**: 只加载需要的Unicode范围
2. **带宽**: 中文字体文件较大，考虑本地字体
3. **回退**: 始终提供多个字体备选
4. **许可**: 确保字体使用许可合规
5. **CDN**: 使用CDN加速字体加载


path: references/style/layers.md
content: 

# 图层类型 (Layers)

样式规范定义的图层类型。

## fill - 填充面

用于渲染多边形区域。

### Paint属性

- `fill-antialias` - 抗锯齿
- `fill-opacity` - 透明度
- `fill-color` - 填充颜色
- `fill-outline-color` - 轮廓颜色
- `fill-translate` - 平移
- `fill-translate-anchor` - 平移锚点(map/viewport)
- `fill-pattern` - 填充图案

### 示例

```javascript
{
  id: 'parks',
  type: 'fill',
  source: 'landuse',
  'source-layer': 'park',
  paint: {
    'fill-color': '#4ade80',
    'fill-opacity': 0.5
  }
}
```

## line - 线

用于渲染线状要素。

### Layout属性

- `line-cap` - 端点样式(butt/round/square)
- `line-join` - 连接样式(bevel/round/miter)
- `line-miter-limit` - 斜接限制
- `line-round-limit` - 圆角限制

### Paint属性

- `line-opacity` - 透明度
- `line-color` - 颜色
- `line-translate` - 平移
- `line-translate-anchor` - 平移锚点
- `line-width` - 宽度
- `line-gap-width` - 间隙宽度
- `line-offset` - 偏移
- `line-blur` - 模糊
- `line-dasharray` - 虚线数组
- `line-pattern` - 线图案
- `line-gradient` - 渐变

### 示例

```javascript
{
  id: 'roads',
  type: 'line',
  source: 'streets',
  layout: {
    'line-cap': 'round',
    'line-join': 'round'
  },
  paint: {
    'line-color': '#3b82f6',
    'line-width': 3,
    'line-dasharray': [2, 2]
  }
}
```

## symbol - 符号/文本

用于渲染图标和文本标签。

### Layout属性

- `symbol-placement` - 符号位置(point/line/line-center)
- `symbol-spacing` - 符号间距
- `symbol-avoid-edges` - 避免边缘
- `symbol-z-order` - Z轴顺序(auto/viewport-y/source)
- `icon-image` - 图标图片
- `icon-rotation-alignment` - 图标旋转对齐
- `icon-size` - 图标大小
- `icon-text-fit` - 图标文本适配
- `icon-padding` - 图标内边距
- `icon-keep-upright` - 保持正立
- `icon-offset` - 图标偏移
- `icon-anchor` - 图标锚点
- `icon-pitch-alignment` - 图标俯仰对齐
- `text-field` - 文本字段
- `text-font` - 字体
- `text-size` - 文本大小
- `text-max-width` - 最大宽度
- `text-line-height` - 行高
- `text-letter-spacing` - 字母间距
- `text-justify` - 对齐方式
- `text-anchor` - 文本锚点
- `text-offset` - 文本偏移
- `text-writing-mode` - 书写模式
- `text-rotation-alignment` - 文本旋转对齐

### Paint属性

- `icon-opacity` - 图标透明度
- `icon-color` - 图标颜色
- `icon-halo-color` - 图标光晕颜色
- `icon-halo-width` - 图标光晕宽度
- `icon-halo-blur` - 图标光晕模糊
- `icon-translate` - 图标平移
- `text-opacity` - 文本透明度
- `text-color` - 文本颜色
- `text-halo-color` - 文本光晕颜色
- `text-halo-width` - 文本光晕宽度
- `text-halo-blur` - 文本光晕模糊
- `text-translate` - 文本平移

### 示例

```javascript
// 图标
{
  id: 'icons',
  type: 'symbol',
  source: 'places',
  layout: {
    'icon-image': 'restaurant-15',
    'icon-size': 1
  }
}

// 文本
{
  id: 'labels',
  type: 'symbol',
  source: 'places',
  layout: {
    'text-field': ['get', 'name'],
    'text-font': ['Open Sans Regular'],
    'text-size': 14
  },
  paint: {
    'text-color': '#333',
    'text-halo-color': '#fff',
    'text-halo-width': 2
  }
}
```

## circle - 圆点

用于渲染圆形标记。

### Paint属性

- `circle-radius` - 半径
- `circle-color` - 颜色
- `circle-blur` - 模糊
- `circle-opacity` - 透明度
- `circle-translate` - 平移
- `circle-translate-anchor` - 平移锚点
- `circle-stroke-width` - 描边宽度
- `circle-stroke-color` - 描边颜色
- `circle-stroke-opacity` - 描边透明度

### 示例

```javascript
{
  id: 'points',
  type: 'circle',
  source: 'data',
  paint: {
    'circle-radius': 8,
    'circle-color': '#3b82f6',
    'circle-stroke-width': 2,
    'circle-stroke-color': '#fff'
  }
}
```

## heatmap - 热力图

用于渲染密度热力图。

### Paint属性

- `heatmap-radius` - 半径
- `heatmap-weight` - 权重
- `heatmap-intensity` - 强度
- `heatmap-color` - 颜色
- `heatmap-opacity` - 透明度

### 示例

```javascript
{
  id: 'heatmap',
  type: 'heatmap',
  source: 'earthquakes',
  paint: {
    'heatmap-weight': [
      'interpolate',
      ['linear'],
      ['get', 'magnitude'],
      0, 0,
      6, 1
    ],
    'heatmap-color': [
      'interpolate',
      ['linear'],
      ['heatmap-density'],
      0, 'rgba(0, 0, 255, 0)',
      0.2, 'royalblue',
      0.4, 'cyan',
      0.6, 'lime',
      0.8, 'yellow',
      1, 'red'
    ],
    'heatmap-radius': 20,
    'heatmap-opacity': 0.8
  }
}
```

## fill-extrusion - 3D挤出

用于渲染3D建筑物等。

### Paint属性

- `fill-extrusion-opacity` - 透明度
- `fill-extrusion-color` - 颜色
- `fill-extrusion-translate` - 平移
- `fill-extrusion-translate-anchor` - 平移锚点
- `fill-extrusion-pattern` - 图案
- `fill-extrusion-height` - 高度
- `fill-extrusion-base` - 基础高度
- `fill-extrusion-vertical-gradient` - 垂直渐变

### 示例

```javascript
{
  id: '3d-buildings',
  type: 'fill-extrusion',
  source: 'buildings',
  'source-layer': 'building',
  minzoom: 15,
  paint: {
    'fill-extrusion-color': '#aaa',
    'fill-extrusion-height': ['get', 'height'],
    'fill-extrusion-base': ['get', 'min_height'],
    'fill-extrusion-opacity': 0.6
  }
}
```

## raster - 栅格

用于渲染栅格瓦片。

### Paint属性

- `raster-opacity` - 透明度
- `raster-hue-rotate` - 色相旋转
- `raster-brightness-min` - 最小亮度
- `raster-brightness-max` - 最大亮度
- `raster-saturation` - 饱和度
- `raster-contrast` - 对比度
- `raster-resampling` - 重采样(linear/nearest)
- `raster-fade-duration` - 淡入时长

### 示例

```javascript
{
  id: 'satellite',
  type: 'raster',
  source: 'satellite-source',
  paint: {
    'raster-opacity': 0.8
  }
}
```

## hillshade - 山体阴影

用于渲染地形阴影。

### Paint属性

- `hillshade-illumination-direction` - 光照方向
- `hillshade-illumination-anchor` - 光照锚点
- `hillshade-exaggeration` - 夸张程度
- `hillshade-shadow-color` - 阴影颜色
- `hillshade-highlight-color` - 高亮颜色
- `hillshade-accent-color` - 强调颜色

### 示例

```javascript
{
  id: 'hillshade',
  type: 'hillshade',
  source: 'terrain',
  paint: {
    'hillshade-exaggeration': 0.5
  }
}
```

## background - 背景

用于渲染地图背景。

### Paint属性

- `background-color` - 颜色
- `background-pattern` - 图案
- `background-opacity` - 透明度

### 示例

```javascript
{
  id: 'background',
  type: 'background',
  paint: {
    'background-color': '#f0f0f0'
  }
}
```


## 注意事项

1. **图层顺序**: 后添加的图层在上层
2. **性能**: 简化表达式,减少复杂计算
3. **可见性**: 使用 visibility 控制显示/隐藏
4. **缩放级别**: 使用 minzoom/maxzoom 优化性能
5. **数据驱动**: 利用表达式实现动态样式


path: references/style/light.md
content: 

# Light - 光照

Light 属性定义全局光源，影响fill-extrusion等3D图层的照明效果。

## 配置

### 基本配置

```javascript
{
  "light": {
    "anchor": "viewport",
    "position": [1, 90, 80],
    "intensity": 0.5,
    "color": "#ffffff"
  }
}
```

### 属性说明

- **anchor**: 光源锚点 (`viewport` 或 `map`)
  - `viewport`: 光源位置相对于视口固定
  - `map`: 光源位置随地图旋转
- **position**: 光源位置 `[x, y, z]`
  - `x`: 东西方向（经度方向）
  - `y`: 南北方向（纬度方向）
  - `z`: 高度（仰角）
- **intensity**: 光照强度 (0-1)
- **color**: 光照颜色

## 动态设置光照

### 基础光照

```javascript
map.setLight({
  anchor: 'viewport',
  position: [1, 90, 80],
  intensity: 0.5,
  color: '#ffffff'
});
```

### 强光照

```javascript
map.setLight({
  anchor: 'viewport',
  position: [1, 90, 80],
  intensity: 1.0,
  color: '#ffffff'
});
```

### 柔和光照

```javascript
map.setLight({
  anchor: 'viewport',
  position: [1, 90, 60],
  intensity: 0.3,
  color: '#ffeecc'
});
```

## 位置参数详解

### Position数组

```javascript
// [方位角, 仰角, 高度]
position: [
  1,    // 方位角（逆时针从北开始）
  90,   // 仰角（0-90度）
  80    // 高度
]
```

### 不同时间段的光照

#### 早晨

```javascript
map.setLight({
  anchor: 'map',
  position: [120, 45, 50],  // 东南方向
  intensity: 0.6,
  color: '#ffeedd'  // 暖色
});
```

#### 中午

```javascript
map.setLight({
  anchor: 'map',
  position: [180, 80, 100],  // 正上方
  intensity: 1.0,
  color: '#ffffff'  // 白光
});
```

#### 傍晚

```javascript
map.setLight({
  anchor: 'map',
  position: [240, 30, 50],  // 西南方向
  intensity: 0.5,
  color: '#ffddaa'  // 金黄色
});
```

## Anchor模式对比

### Viewport模式

```javascript
// 光源位置相对于屏幕固定
map.setLight({
  anchor: 'viewport',
  position: [1, 90, 80],
  intensity: 0.5
});

// 旋转地图时，光照方向不变
map.setBearing(45);
```

### Map模式

```javascript
// 光源位置相对于地图固定
map.setLight({
  anchor: 'map',
  position: [180, 80, 100],
  intensity: 0.5
});

// 旋转地图时，光照方向随之改变
map.setBearing(45);
```

## 日照动画

### 模拟太阳运动

```javascript
class SunLight {
  constructor(map) {
    this.map = map;
    this.time = 0;
    this.duration = 60000; // 1分钟一圈
  }
  
  start() {
    this.animate();
  }
  
  animate() {
    this.time += 16;
    const progress = (this.time % this.duration) / this.duration;
    
    // 计算太阳位置
    const azimuth = progress * 360;  // 方位角 0-360度
    const altitude = Math.max(10, Math.sin(progress * Math.PI) * 80);  // 仰角
    
    // 计算颜色（早晨/傍晚偏暖，中午偏白）
    const warmth = Math.sin(progress * Math.PI);
    const color = `rgb(255, ${200 + warmth * 55}, ${150 + warmth * 105})`;
    
    // 计算强度
    const intensity = 0.3 + warmth * 0.7;
    
    this.map.setLight({
      anchor: 'map',
      position: [azimuth, altitude, 100],
      intensity: intensity,
      color: color
    });
    
    requestAnimationFrame(() => this.animate());
  }
}

const sun = new SunLight(map);
sun.start();
```

## 配合3D建筑物

```javascript
// 添加3D建筑物
map.addLayer({
  id: '3d-buildings',
  type: 'fill-extrusion',
  source: 'composite',
  'source-layer': 'building',
  minzoom: 15,
  paint: {
    'fill-extrusion-color': '#aaa',
    'fill-extrusion-height': ['get', 'height'],
    'fill-extrusion-base': ['get', 'min_height'],
    'fill-extrusion-opacity': 0.8
  }
});

// 设置光照
map.setLight({
  anchor: 'viewport',
  position: [1, 60, 70],
  intensity: 0.7,
  color: '#ffffff'
});
```

## 特殊效果

### 月光效果

```javascript
map.setLight({
  anchor: 'viewport',
  position: [1, 45, 50],
  intensity: 0.2,
  color: '#ccccff'  // 冷蓝色
});
```

### 霓虹灯效果

```javascript
map.setLight({
  anchor: 'viewport',
  position: [1, 90, 80],
  intensity: 0.8,
  color: '#ff00ff'  // 紫色
});
```

### 戏剧性光照

```javascript
map.setLight({
  anchor: 'map',
  position: [45, 30, 50],  // 低角度
  intensity: 1.0,
  color: '#ff6600'  // 橙色
});
```

## 注意事项

1. **3D图层**: 光照主要影响fill-extrusion图层
2. **性能**: 动态光照会影响性能
3. **Anchor选择**: 根据需求选择viewport或map
4. **颜色搭配**: 光照颜色与建筑物颜色协调
5. **强度控制**: 过强的光照会过曝
6. **动画频率**: 避免过快更新光照


path: references/style/other.md
content: 

# Other - 其他样式属性

其他未在专门章节中描述的样式属性和概念。

## Functions - 函数

属性值可以是函数，根据缩放级别或数据属性动态计算。

### Zoom Functions（已废弃，使用表达式）

旧版语法（不推荐）:

```javascript
// 已废弃，使用interpolate表达式替代
'line-width': {
  "base": 1.5,
  "stops": [
    [5, 1],
    [10, 2],
    [15, 5]
  ]
}
```

新版语法（推荐）:

```javascript
'line-width': [
  'interpolate',
  ['exponential', 1.5],
  ['zoom'],
  5, 1,
  10, 2,
  15, 5
]
```

## Data-driven Styling - 数据驱动样式

基于要素属性动态设置样式。

### 基础示例

```javascript
'circle-color': ['get', 'color']
'circle-radius': ['get', 'size']
```

### 条件样式

```javascript
'fill-color': [
  'case',
  ['==', ['get', 'type'], 'residential'], '#ff0000',
  ['==', ['get', 'type'], 'commercial'], '#00ff00',
  '#0000ff'  // 默认值
]
```

详见 [Expressions](expressions.md)

## Filters - 过滤器

过滤要显示的要素。

### 基础过滤

```javascript
// 只显示特定类型
'filter': ['==', 'type', 'city']

// 数值比较
'filter': ['>', 'population', 1000000]
```

### 复合过滤

```javascript
// AND
'filter': [
  'all',
  ['==', 'type', 'city'],
  ['>', 'population', 1000000]
]

// OR
'filter': [
  'any',
  ['==', 'type', 'city'],
  ['==', 'type', 'town']
]

// NOT
'filter': [
  '!',
  ['==', 'type', 'village']
]
```

### 动态更新过滤器

```javascript
// 设置过滤器
map.setFilter('cities', ['>', 'population', 500000]);

// 清除过滤器
map.setFilter('cities', null);
```

## Visibility - 可见性

控制图层的显示/隐藏。

### 设置可见性

```javascript
// 隐藏图层
map.setLayoutProperty('my-layer', 'visibility', 'none');

// 显示图层
map.setLayoutProperty('my-layer', 'visibility', 'visible');
```

### 切换可见性

```javascript
function toggleLayer(layerId) {
  const visibility = map.getLayoutProperty(layerId, 'visibility');
  map.setLayoutProperty(
    layerId,
    'visibility',
    visibility === 'visible' ? 'none' : 'visible'
  );
}
```

## MinZoom / MaxZoom - 缩放范围

控制图层在特定缩放级别范围内显示。

### 配置

```javascript
{
  "id": "detailed-roads",
  "type": "line",
  "source": "streets",
  "minzoom": 12,  // 仅在zoom >= 12时显示
  "maxzoom": 20,  // 仅在zoom < 20时显示
  "paint": {
    "line-color": "#3b82f6"
  }
}
```

### 动态修改

```javascript
// 注意：minzoom/maxzoom不能在运行时修改
// 需要在添加图层时指定
```

## Source Layer - 矢量瓦片图层

指定矢量瓦片中的子图层。

### 配置

```javascript
{
  "id": "buildings",
  "type": "fill",
  "source": "composite",
  "source-layer": "building",  // 矢量瓦片中的图层名
  "paint": {
    "fill-color": "#cccccc"
  }
}
```


## Paint Properties - 绘制属性

控制图层的视觉表现。

### 通用属性

- `*-opacity`: 透明度
- `*-color`: 颜色
- `*-translate`: 平移
- `*-translate-anchor`: 平移锚点

### 过渡属性

每个可动画的paint属性都有对应的transition:

```javascript
'fill-opacity-transition': {
  duration: 300,
  delay: 0
}
```

## Layout Properties - 布局属性

控制图层的布局和渲染方式。

### 通用属性

- `visibility`: 可见性
- 各图层类型特有的布局属性


## 注意事项

1. **向后兼容**: 避免使用已废弃的特性
2. **性能**: 复杂的过滤器影响性能
3. **表达式优先**: 使用表达式替代旧版functions
4. **文档**: 参考各专题文档获取详细信息
5. **验证**: 使用样式验证工具
6. **测试**: 在不同缩放级别测试样式


path: references/style/terrain.md
content: 

# Terrain - 地形

Terrain 属性定义全局地形高程数据源。

## 配置

### 基本配置

```javascript
{
  "terrain": {
    "source": "dmap-dem",
    "exaggeration": 1.5
  }
}
```

### 属性说明

- **source** (必需): DEM数据源ID
- **exaggeration** (可选): 地形夸张倍数，默认1.0

## 添加DEM数据源


### 自定义DEM数据

```javascript
map.addSource('custom-dem', {
  type: 'raster-dem',
  tiles: [
    'https://example.com/terrain/{z}/{x}/{y}.png'
  ],
  tileSize: 256,
  encoding: 'terrarium',  // 或 'dmap'
  minzoom: 7,
  maxzoom: 12
});

map.setTerrain({
  source: 'custom-dem',
  exaggeration: 2.0
});
```

## DEM编码格式

### Mapbox编码

- RGB值编码高程
- 公式: `height = -10000 + ((R * 256 * 256 + G * 256 + B) * 0.1)`
- 范围: -10000米到15000米

### Terrarium编码

- PNG格式
- 公式: `height = (R * 256 + G + B / 256) - 32768`
- 范围: -32768米到32767米

## 动态控制地形

### 调整夸张度

```javascript
// 增加地形起伏
map.setTerrain({
  source: 'dmap-dem',
  exaggeration: 2.5
});

// 减少地形起伏
map.setTerrain({
  source: 'dmap-dem',
  exaggeration: 0.5
});

// 移除地形
map.setTerrain(null);
```

### 动画效果

```javascript
let exaggeration = 0;
const targetExaggeration = 2.0;

function animateTerrain() {
  exaggeration += 0.1;
  
  if (exaggeration <= targetExaggeration) {
    map.setTerrain({
      source: 'dmap-dem',
      exaggeration: exaggeration
    });
    
    requestAnimationFrame(animateTerrain);
  }
}

animateTerrain();
```

## 配合3D图层

### 3D建筑物

```javascript
map.addLayer({
  id: '3d-buildings',
  type: 'fill-extrusion',
  source: 'composite',
  'source-layer': 'building',
  minzoom: 15,
  paint: {
    'fill-extrusion-color': '#aaa',
    'fill-extrusion-height': ['get', 'height'],
    'fill-extrusion-base': ['get', 'min_height'],
    'fill-extrusion-opacity': 0.6
  }
});
```

### 天空雾层

```javascript
map.setFog({
  'range': [1, 5],
  'horizon-blend': 0.1,
  'color': 'white',
  'high-color': '#30ccff',
  'space-color': '#ade0fa',
  'star-intensity': 0.0
});
```

## 视角设置

```javascript
// 设置3D视角
map.setPitch(60);
map.setBearing(-20);
map.setZoom(16);
map.setCenter([800000, 600000]);
```

## 注意事项

1. **性能**: 地形渲染需要GPU支持
2. **数据大小**: DEM瓦片较大，注意带宽
3. **缩放级别**: 高缩放级别效果更好
4. **兼容性**: 检查WebGL支持
5. **夸张度**: 过高的夸张度可能失真
6. **内存**: 地形数据占用较多显存


path: references/style/fog.md
content: 

# Fog - 雾效

Fog 属性定义全局大气雾效，增强深度感和真实感。

## 配置

### 基本配置

```javascript
{
  "fog": {
    "range": [0.5, 10],
    "color": "#ffffff",
    "horizon-blend": 0.1
  }
}
```

### 属性说明

- **range**: 雾的可见范围 `[near, far]`，相对于地平线距离
- **color**: 雾的颜色
- **horizon-blend**: 地平线混合度 (0-1)
- **high-color**: 高空颜色（可选）
- **space-color**: 太空颜色（可选）
- **star-intensity**: 星星强度 (0-1)（可选）

## 动态设置雾效

### 白天雾效

```javascript
map.setFog({
  range: [0.5, 10],
  color: '#ffffff',
  'horizon-blend': 0.1
});
```

### 夜晚雾效

```javascript
map.setFog({
  range: [0.5, 15],
  color: '#000033',
  'high-color': '#000066',
  'horizon-blend': 0.2,
  'star-intensity': 0.6
});
```

### 黄昏雾效

```javascript
map.setFog({
  range: [1, 12],
  color: '#ff9966',
  'high-color': '#ff6633',
  'horizon-blend': 0.15
});
```

## 属性详解

### range - 雾的范围

```javascript
// [近端距离, 远端距离]
// 值越小，雾越浓
map.setFog({
  range: [0.2, 5],  // 浓雾
  color: '#cccccc'
});

map.setFog({
  range: [1, 20],  // 薄雾
  color: '#eeeeee'
});
```

### horizon-blend - 地平线混合

```javascript
// 0 = 无混合，清晰的地平线
// 1 = 完全混合，模糊的地平线
map.setFog({
  range: [0.5, 10],
  color: '#ffffff',
  'horizon-blend': 0.05  // 清晰地平线
});

map.setFog({
  range: [0.5, 10],
  color: '#ffffff',
  'horizon-blend': 0.5  // 模糊的地平线
});
```

### high-color - 高空颜色

```javascript
map.setFog({
  range: [0.5, 10],
  color: '#ffffff',
  'high-color': '#87ceeb',  // 天空蓝
  'horizon-blend': 0.2
});
```

### space-color - 太空颜色

```javascript
map.setFog({
  range: [1, 10],
  color: '#000011',
  'high-color': '#000033',
  'space-color': '#000000',  // 黑色太空
  'star-intensity': 0.8
});
```

### star-intensity - 星星强度

```javascript
// 0 = 无星星
// 1 = 最强星星
map.setFog({
  range: [1, 15],
  color: '#000022',
  'space-color': '#000000',
  'star-intensity': 1.0  // 满天繁星
});
```

## 昼夜循环示例

```javascript
class DayNightFog {
  constructor(map) {
    this.map = map;
    this.time = 0;
    this.duration = 60000; // 1分钟一个周期
  }
  
  start() {
    this.animate();
  }
  
  animate() {
    this.time += 16;
    const progress = (this.time % this.duration) / this.duration;
    const sunAngle = progress * Math.PI * 2;
    const sinValue = Math.sin(sunAngle);
    
    if (sinValue > 0.3) {
      // 白天
      this.map.setFog({
        range: [0.5, 10],
        color: '#ffffff',
        'horizon-blend': 0.1
      });
    } else if (sinValue > -0.3) {
      // 黄昏/黎明
      this.map.setFog({
        range: [0.8, 12],
        color: '#ff9966',
        'high-color': '#ff6633',
        'horizon-blend': 0.15
      });
    } else {
      // 夜晚
      this.map.setFog({
        range: [0.5, 15],
        color: '#000033',
        'high-color': '#000066',
        'space-color': '#000000',
        'star-intensity': 0.7,
        'horizon-blend': 0.2
      });
    }
    
    requestAnimationFrame(() => this.animate());
  }
}

const fogCycle = new DayNightFog(map);
fogCycle.start();
```

## 天气效果

### 雾霾天气

```javascript
map.setFog({
  range: [0.1, 3],
  color: '#dddddd',
  'horizon-blend': 0.4
});
```

### 晴朗天气

```javascript
map.setFog({
  range: [1, 20],
  color: '#f0f8ff',
  'high-color': '#87ceeb',
  'horizon-blend': 0.05
});
```

### 移除雾效

```javascript
map.setFog(null);
```

## 配合地形

```javascript
// 启用地形
map.setTerrain({
  source: 'dmap-dem',
  exaggeration: 1.5
});

// 添加雾效增强深度感
map.setFog({
  range: [0.5, 10],
  color: '#ffffff',
  'horizon-blend': 0.1
});

// 设置观察角度
map.setPitch(60);
map.setZoom(14);
```

## 注意事项

1. **性能**: 雾效会增加渲染开销
2. **范围调整**: 根据缩放级别调整range
3. **颜色选择**: 考虑整体色调协调
4. **星空效果**: 需要深色背景才能看到星星
5. **兼容性**: 旧设备可能不支持
6. **动画**: 避免频繁更新雾效参数


path: references/style/types.md
content: 

# Types - 数据类型

样式规范中使用的各种数据类型。

## Color - 颜色

### 颜色格式

```javascript
// CSS颜色名称
'fill-color': 'red'

// 十六进制
'fill-color': '#ff0000'
'fill-color': '#f00'

// RGB
'fill-color': 'rgb(255, 0, 0)'

// RGBA
'fill-color': 'rgba(255, 0, 0, 0.5)'

// HSL
'fill-color': 'hsl(0, 100%, 50%)'

// HSLA
'fill-color': 'hsla(0, 100%, 50%, 0.5)'
```

### 颜色表达式

```javascript
'fill-color': [
  'interpolate',
  ['linear'],
  ['get', 'temperature'],
  0, 'blue',
  20, 'green',
  40, 'yellow',
  60, 'red'
]
```

## PromoteId - ID提升

用于从要素属性中指定唯一标识符。

### 配置

```javascript
{
  "type": "vector",
  "url": dmapgl.serviceUrl + "/map/vector/standard/styles/style.json",
  "promoteId": {
    "building": "building_id"
  }
}
```

### 使用场景

```javascript
// GeoJSON源
map.addSource('buildings', {
  type: 'geojson',
  data: buildingData,
  promoteId: 'id'  // 使用'id'属性作为要素ID
});

// 设置要素状态
map.setFeatureState(
  { source: 'buildings', id: 123 },
  { hover: true }
);
```

## Formatted - 格式化文本

支持富文本格式化的字符串。

### 基本用法

```javascript
'text-field': [
  'format',
  'Bold Text', { 'font-scale': 1.2, 'text-font': ['Open Sans Bold'] },
  '\n', {},
  'Normal Text', { 'font-scale': 1.0 }
]
```

### 带颜色的文本

```javascript
'text-field': [
  'format',
  'Red ', { 'text-color': '#ff0000' },
  'Green ', { 'text-color': '#00ff00' },
  'Blue', { 'text-color': '#0000ff' }
]
```

### 动态格式化

```javascript
'text-field': [
  'format',
  ['get', 'name'], { 'font-scale': 1.2 },
  '\n', {},
  ['get', 'population'], { 
    'font-scale': 0.8,
    'text-color': '#666666'
  }
]
```

## ResolvedImage - 解析图片

用于图标图片的引用。

### 基本用法

```javascript
'icon-image': 'restaurant-15'
```

### 条件图片

```javascript
'icon-image': [
  'match',
  ['get', 'type'],
  'restaurant', 'restaurant-15',
  'cafe', 'cafe-15',
  'hotel', 'hotel-15',
  'default-15'  // 默认值
]
```

### 动态添加图片

```javascript
// 从URL加载
const image = new Image();
image.src = 'custom-icon.png';
image.onload = () => {
  map.addImage('custom-icon', image);
};

// 使用
'icon-image': 'custom-icon'
```

## VariableAnchorOffsetCollection - 可变锚点偏移

定义文本标签的可变锚点和偏移。

### 配置

```javascript
'text-variable-anchor-offset': [
  'top', [0, -10],
  'bottom', [0, 10],
  'left', [-10, 0],
  'right', [10, 0]
]
```

### 使用场景

```javascript
map.addLayer({
  id: 'labels',
  type: 'symbol',
  source: 'places',
  layout: {
    'text-field': ['get', 'name'],
    'text-variable-anchor': ['top', 'bottom', 'left', 'right'],
    'text-radial-offset': 1.5
  }
});
```

## 数值类型

### Number

```javascript
'circle-radius': 10
'fill-opacity': 0.7
'line-width': 3
```

### 数组

```javascript
// 坐标数组
'center': [800000, 600000]

// 边界数组
'bounds': [716638.24, 548483.59, 894455.08, 728066.47]

// 颜色数组（RGBA）
'fill-color': [255, 0, 0, 0.5]
```

## 字符串类型

### String

```javascript
'text-field': 'Hello World'
'icon-image': 'marker-15'
```

### Enum（枚举）

```javascript
// visibility
'visibility': 'visible'  // 或 'none'

// line-cap
'line-cap': 'round'  // 'butt', 'round', 'square'

// line-join
'line-join': 'round'  // 'bevel', 'round', 'miter'

// symbol-placement
'symbol-placement': 'point'  // 'point', 'line', 'line-center'
```

## Boolean类型

```javascript
'circle-antialias': true
'icon-allow-overlap': false
```

## Padding - 内边距

```javascript
// 单一值
'icon-padding': 10

// 数组 [上, 右, 下, 左]
'icon-padding': [10, 20, 10, 20]
```

## Offset - 偏移

```javascript
// [x, y] 偏移
'icon-offset': [10, -20]
'text-offset': [0, -1.5]
```

## Anchor - 锚点

```javascript
// 单锚点
'icon-anchor': 'center'  // center, left, right, top, bottom等

// 多锚点
'text-variable-anchor': ['top', 'bottom', 'left', 'right']
```

## 注意事项

1. **类型安全**: 确保属性值类型正确
2. **单位**: 注意不同属性的单位（像素、度、米等）
3. **范围**: 某些属性有有效范围限制
4. **兼容性**: 检查浏览器对颜色格式的支持
5. **性能**: 复杂的格式化文本影响渲染性能


path: references/style/expressions.md
content: 

# 表达式 (Expressions)

表达式用于动态计算样式属性值,支持数据驱动样式。

## 表达式类型

### 查找类

#### get - 获取属性值

```javascript
// 获取属性
'circle-color': ['get', 'color']

// 嵌套属性
'text-field': ['get', 'name.en']
```

#### has - 检查属性是否存在

```javascript
'circle-color': [
  'case',
  ['has', 'color'],
  ['get', 'color'],
  '#3b82f6'  // 默认值
]
```

#### in - 检查值是否在数组/字符串中

```javascript
'circle-color': [
  'case',
  ['in', ['get', 'type'], 'city', 'town'],
  '#ff0000',
  '#00ff00'
]
```

### 条件类

#### case - 条件判断

```javascript
'circle-color': [
  'case',
  ['>', ['get', 'population'], 1000000], '#ff0000',  // 如果人口>100万
  ['>', ['get', 'population'], 500000], '#ffff00',   // 如果人口>50万
  '#00ff00'  // 默认值
]
```

#### match - 匹配

```javascript
'circle-color': [
  'match',
  ['get', 'type'],
  'city', '#ff0000',
  'town', '#ffff00',
  'village', '#00ff00',
  '#999999'  // 默认值
]
```

#### coalesce - 返回第一个非null值

```javascript
'text-field': ['coalesce', ['get', 'name_en'], ['get', 'name']]
```


#### within - 检查要素是否在几何范围内

返回 true 如果要素完全包含在输入几何的边界内,否则返回 false。
输入值可以是有效的 GeoJSON,类型为 Polygon、MultiPolygon、Feature 或 FeatureCollection。

**支持的要素类型:**
- **Point**: 如果点在边界上或边界外,返回 false
- **LineString**: 如果线的任何部分在边界外、线与边界相交、或线的端点在边界上,返回 false

```javascript
// 定义一个多边形边界
const boundary = {
  type: 'Polygon',
  coordinates: [[
    [800000, 600000],
    [810000, 600000],
    [810000, 610000],
    [800000, 610000],
    [800000, 600000]
  ]]
};

// 在过滤器中使用
'filter': ['within', boundary]

// 只在指定区域内的要素显示
map.addLayer({
  id: 'points-in-area',
  type: 'circle',
  source: 'points',
  filter: ['within', boundary],
  paint: {
    'circle-radius': 5,
    'circle-color': '#3b82f6'
  }
});
```

**使用 Feature:**

```javascript
const feature = {
  type: 'Feature',
  geometry: {
    type: 'Polygon',
    coordinates: [[
      [800000, 600000],
      [810000, 600000],
      [810000, 610000],
      [800000, 610000],
      [800000, 600000]
    ]]
  }
};

'filter': ['within', feature]
```

**动态更新范围:**

```javascript
// 更新过滤器的范围
function updateWithinFilter(newBoundary) {
  map.setFilter('points-in-area', ['within', newBoundary]);
}
```


### 数学运算类

#### interpolate - 插值

```javascript
// 线性插值
'circle-radius': [
  'interpolate',
  ['linear'],
  ['zoom'],
  5, 5,    // zoom=5时半径=5
  10, 10,  // zoom=10时半径=10
  15, 20   // zoom=15时半径=20
]

// 指数插值
'line-width': [
  'interpolate',
  ['exponential', 1.5],
  ['zoom'],
  5, 1,
  10, 2,
  15, 5
]
```

#### step - 阶梯函数

```javascript
'circle-radius': [
  'step',
  ['get', 'population'],
  5,           // 默认值
  100000, 10,  // population >= 100000 时半径=10
  1000000, 20  // population >= 1000000 时半径=20
]
```

#### +, -, *, /, % - 算术运算

```javascript
'circle-radius': ['+', ['get', 'base_radius'], 2]
'line-opacity': ['/', ['get', 'value'], 100]
'area-ratio': ['%', ['get', 'area'], 100]
```

#### ^, sqrt, abs, floor, ceil, round - 数学函数

```javascript
'circle-radius': ['sqrt', ['get', 'area']]
'text-size': ['floor', ['get', 'size']]
'rounded-value': ['round', ['get', 'value']]
```

#### min, max - 最小最大值

```javascript
'circle-radius': ['min', ['get', 'radius'], 20]  // 最大20
'line-width': ['max', ['get', 'width'], 1]       // 最小1
```

#### clamp - 限制范围

```javascript
// 将值限制在[min, max]范围内
'circle-opacity': ['clamp', ['get', 'opacity'], 0.2, 0.8]
```

### 逻辑运算类

#### all, any, ! - 逻辑与或非

```javascript
// AND
['all', 
  ['==', ['get', 'type'], 'city'],
  ['>', ['get', 'population'], 1000000]
]

// OR
['any',
  ['==', ['get', 'type'], 'city'],
  ['==', ['get', 'type'], 'town']
]

// NOT
['!', ['has', 'hidden']]
```

#### ==, !=, >, >=, <, <= - 比较运算

```javascript
'circle-color': [
  'case',
  ['>=', ['get', 'rating'], 4], '#00ff00',
  ['>=', ['get', 'rating'], 3], '#ffff00',
  '#ff0000'
]
```

### 字符串类

#### concat - 拼接

```javascript
'text-field': ['concat', ['get', 'name'], ' (', ['get', 'type'], ')']
```

#### format - 格式化文本

用于在 symbol 层的 text-field 中创建富文本格式。

```javascript
'text-field': [
  'format',
  ['get', 'name'], { 'font-scale': 1.2, 'text-font': ['Open Sans Bold'] },
  '\n', {},
  ['get', 'population'], { 'font-scale': 0.8, 'text-color': '#666' }
]
```

**format 选项:**
- `font-scale`: 字体缩放比例
- `text-font`: 字体数组
- `text-color`: 文本颜色
- `font-stretch`: 字体拉伸
- `font-weight`: 字体粗细

#### downcase, upcase - 大小写转换

```javascript
'text-field': ['upcase', ['get', 'name']]
```

#### length - 长度

```javascript
'label-density': ['length', ['get', 'name']]
```

#### substring - 子字符串

```javascript
'text-field': ['substring', ['get', 'code'], 0, 3]
```

#### match - 正则匹配

```javascript
// 检查字符串是否匹配正则表达式
'filter': ['match', ['get', 'name'], '^[A-Z].*', true, false]

// 提取匹配部分
['match', ['get', 'phone'], '(\d{3})-(\d{3})-(\d{4})', '$1$2$3', 'invalid']
```


### 颜色类

#### rgb, rgba - RGB颜色

```javascript
'circle-color': ['rgb', 255, 0, 0]
'fill-opacity': ['rgba', 255, 0, 0, 0.5]
```

#### to-rgba - 转换为RGBA数组

```javascript
['to-rgba', ['get', 'color']]
// 返回 [r, g, b, a] 数组
```

#### hsl, hsla - HSL颜色

```javascript
'circle-color': ['hsl', 120, 100, 50]  // 绿色
'fill-color': ['hsla', 240, 100, 50, 0.5]  // 半透明蓝色
```

### 类型转换类

#### to-string, to-number, to-boolean, to-color

```javascript
'text-field': ['to-string', ['get', 'id']]
'circle-radius': ['to-number', ['get', 'radius'], 5]
```

#### literal - 字面量数组

```javascript
// 当需要传递数组字面量时使用
'filter': ['in', 1, ['literal', [1, 2, 3]]]

// 不需要literal的情况(子表达式返回的数组)
'filter': ['in', 1, ['get', 'myArrayProp']]
```

#### collator - 排序规则

```javascript
// 不区分大小写排序
['sort-key', ['collator', {'case-sensitive': false}], ['get', 'name']]

// 区分重音符号
['sort-key', ['collator', {'diacritic-sensitive': true}], ['get', 'name']]
```

### 数组类

#### array, at, slice, index-of, in

```javascript
// 创建数组
['array', 1, 2, 3]

// 获取元素
['at', 0, ['get', 'colors']]

// 切片
['slice', ['get', 'tags'], 0, 3]

// 索引
['index-of', 'red', ['get', 'colors']]
```

#### length - 数组长度

```javascript
'text-field': ['concat', '共有', ['length', ['get', 'tags']], '个标签']
```

#### concat (数组) - 数组合并

```javascript
// 合并多个数组
['concat', ['get', 'tags1'], ['get', 'tags2']]
```

### 特性状态

#### feature-state - 获取要素状态

```javascript
'fill-color': [
  'case',
  ['boolean', ['feature-state', 'hover'], false],
  '#ff0000',
  '#3b82f6'
]
```

### 缩放级别

#### zoom - 当前缩放级别

```javascript
'line-width': [
  'interpolate',
  ['linear'],
  ['zoom'],
  5, 1,
  10, 2,
  15, 5
]
```

### 几何信息

#### geometry-type - 几何类型

```javascript
'symbol-placement': [
  'match',
  ['geometry-type'],
  'Point', 'point',
  'LineString', 'line',
  'point'
]
```

#### properties - 要素属性

```javascript
['properties']  // 返回整个properties对象
```

### 变量绑定

#### let - 定义变量

```javascript
'circle-radius': [
  'let',
  'base-size', ['get', 'size'],
  'scale-factor', 2,
  ['*', ['var', 'base-size'], ['var', 'scale-factor']]
]
```

#### var - 引用变量

```javascript
// 必须与let配合使用
['var', 'variable-name']
```

### 累积类(用于聚类)

#### accumulated - 获取累积值

```javascript
// 在聚类中使用
'circle-radius': [
  'case',
  ['has', 'point_count'],
  ['interpolate', ['linear'], ['get', 'point_count'],
    1, 10,
    10, 20,
    100, 30
  ],
  5
]
```

### 热力图专用

#### heatmap-density - 热力图密度

```javascript
'heatmap-color': [
  'interpolate',
  ['linear'],
  ['heatmap-density'],
  0, 'rgba(0, 0, 255, 0)',
  0.2, 'royalblue',
  0.4, 'cyan',
  0.6, 'lime',
  0.8, 'yellow',
  1, 'red'
]
```

### 距离相关

#### distance-from-center - 距中心距离

```javascript
// 仅在symbol层的filter中支持
'filter': ['<', ['distance-from-center'], 1000]
```

### Pitch相关

#### pitch - 当前俯仰角

```javascript
// 仅在symbol层的filter中支持
'filter': ['>', ['pitch'], 45]
```

## 实用示例

### 数据驱动颜色

```javascript
{
  id: 'cities',
  type: 'circle',
  source: 'places',
  paint: {
    'circle-color': [
      'match',
      ['get', 'type'],
      'capital', '#ff0000',
      'city', '#00ff00',
      'town', '#0000ff',
      '#999999'
    ]
  }
}
```

### 缩放驱动样式

```javascript
{
  id: 'roads',
  type: 'line',
  source: 'streets',
  paint: {
    'line-width': [
      'interpolate',
      ['exponential', 1.5],
      ['zoom'],
      5, 0.5,
      10, 2,
      15, 6,
      20, 12
    ],
    'line-color': [
      'interpolate',
      ['linear'],
      ['zoom'],
      10, '#cccccc',
      15, '#666666'
    ]
  }
}
```

### 复合表达式

```javascript
{
  id: 'labels',
  type: 'symbol',
  source: 'places',
  paint: {
    'text-color': [
      'case',
      ['all',
        ['==', ['get', 'type'], 'city'],
        ['>', ['get', 'population'], 1000000]
      ],
      '#ff0000',
      
      ['any',
        ['==', ['get', 'type'], 'town'],
        ['==', ['get', 'type'], 'village']
      ],
      '#00ff00',
      
      '#333333'
    ]
  }
}
```

### 热力图权重

```javascript
{
  id: 'heatmap',
  type: 'heatmap',
  source: 'earthquakes',
  paint: {
    'heatmap-weight': [
      'interpolate',
      ['linear'],
      ['get', 'magnitude'],
      0, 0,
      4, 0.5,
      6, 1
    ]
  }
}
```

### 文本格式化

```javascript
{
  id: 'labels',
  type: 'symbol',
  source: 'places',
  layout: {
    'text-field': [
      'format',
      ['get', 'name'], { 'font-scale': 1.2 },
      '\n', {},
      ['get', 'population'], { 'font-scale': 0.8 }
    ]
  }
}
```

## 注意事项

1. **性能**: 避免过于复杂的嵌套表达式
2. **可读性**: 使用注释和格式化提高可读性
3. **类型安全**: 确保表达式返回正确的类型
4. **默认值**: 提供合理的默认值
5. **测试**: 在不同缩放级别和数据下测试表达式


path: references/style/transition.md
content: 

# Transition - 过渡动画

Transition 属性控制样式属性变化的过渡动画效果。

## 配置

### 全局Transition

```javascript
{
  "transition": {
    "duration": 300,
    "delay": 0
  }
}
```

### 属性说明

- **duration**: 过渡持续时间（毫秒），默认300
- **delay**: 过渡延迟时间（毫秒），默认0

## 图层级Transition

### 在Paint属性中使用

```javascript
map.addLayer({
  id: 'buildings',
  type: 'fill',
  source: 'landuse',
  paint: {
    'fill-color': '#cccccc',
    'fill-opacity': 0.7,
    'fill-opacity-transition': {
      duration: 500,
      delay: 100
    }
  }
});
```

### 禁用Transition

```javascript
map.addLayer({
  id: 'roads',
  type: 'line',
  source: 'streets',
  paint: {
    'line-color': '#3b82f6',
    'line-width': 3,
    'line-width-transition': {
      duration: 0  // 立即变化，无动画
    }
  }
});
```

## 动态设置Transition

### 修改全局Transition

```javascript
map.setTransition({
  duration: 1000,  // 1秒
  delay: 200       // 延迟200毫秒
});
```

### 获取当前Transition

```javascript
const transition = map.getTransition();
console.log('Duration:', transition.duration);
console.log('Delay:', transition.delay);
```

## 可过渡的属性

以下paint属性支持transition:

### Fill图层

- `fill-opacity`
- `fill-color`
- `fill-outline-color`
- `fill-translate`
- `fill-pattern`

### Line图层

- `line-opacity`
- `line-color`
- `line-width`
- `line-gap-width`
- `line-offset`
- `line-blur`
- `line-dasharray`
- `line-translate`
- `line-pattern`

### Symbol图层

- `icon-opacity`
- `icon-color`
- `icon-halo-color`
- `icon-halo-width`
- `icon-halo-blur`
- `icon-translate`
- `text-opacity`
- `text-color`
- `text-halo-color`
- `text-halo-width`
- `text-halo-blur`
- `text-translate`

### Circle图层

- `circle-radius`
- `circle-color`
- `circle-blur`
- `circle-opacity`
- `circle-translate`
- `circle-stroke-width`
- `circle-stroke-color`
- `circle-stroke-opacity`

### 其他图层

- `raster-opacity`
- `raster-hue-rotate`
- `raster-brightness-min`
- `raster-brightness-max`
- `raster-saturation`
- `raster-contrast`
- `fill-extrusion-opacity`
- `fill-extrusion-color`
- `fill-extrusion-height`
- `fill-extrusion-base`

## 实用示例

### 平滑颜色过渡

```javascript
// 设置较长的过渡时间
map.setTransition({
  duration: 2000,
  delay: 0
});

// 改变颜色会平滑过渡
map.setPaintProperty('buildings', 'fill-color', '#ff0000');
```

### 快速响应交互

```javascript
// 悬停时快速高亮
map.on('mouseenter', 'buildings', () => {
  map.setTransition({ duration: 100 });
  map.setPaintProperty('buildings', 'fill-color', '#ffff00');
});

// 离开时缓慢恢复
map.on('mouseleave', 'buildings', () => {
  map.setTransition({ duration: 500 });
  map.setPaintProperty('buildings', 'fill-color', '#cccccc');
});
```

### 渐进式显示

```javascript
// 添加延迟实现渐进效果
map.addLayer({
  id: 'layer1',
  type: 'fill',
  source: 'data1',
  paint: {
    'fill-opacity': 0,
    'fill-opacity-transition': {
      duration: 1000,
      delay: 0
    }
  }
});

map.addLayer({
  id: 'layer2',
  type: 'fill',
  source: 'data2',
  paint: {
    'fill-opacity': 0,
    'fill-opacity-transition': {
      duration: 1000,
      delay: 500  // 延迟500ms
    }
  }
});

// 同时触发，但layer2会延迟显示
map.setPaintProperty('layer1', 'fill-opacity', 1);
map.setPaintProperty('layer2', 'fill-opacity', 1);
```

### 淡入淡出效果

```javascript
function fadeIn(layerId, duration = 1000) {
  map.setTransition({ duration });
  map.setPaintProperty(layerId, 'fill-opacity', 1);
}

function fadeOut(layerId, duration = 1000) {
  map.setTransition({ duration });
  map.setPaintProperty(layerId, 'fill-opacity', 0);
}

// 使用
fadeIn('my-layer', 1500);
setTimeout(() => fadeOut('my-layer', 1500), 3000);
```

## 性能优化

### 禁用不必要的Transition

```javascript
// 对于频繁更新的属性，禁用transition
map.addLayer({
  id: 'realtime-data',
  type: 'circle',
  source: 'live-data',
  paint: {
    'circle-radius': 5,
    'circle-radius-transition': {
      duration: 0  // 禁用过渡
    },
    'circle-color': '#3b82f6',
    'circle-color-transition': {
      duration: 0  // 禁用过渡
    }
  }
});
```

### 批量更新

```javascript
// 先设置transition
map.setTransition({ duration: 500 });

// 批量更新多个属性
map.setPaintProperty('layer1', 'fill-color', '#ff0000');
map.setPaintProperty('layer1', 'fill-opacity', 0.8);
map.setPaintProperty('layer2', 'line-color', '#00ff00');
map.setPaintProperty('layer2', 'line-width', 5);
```

## 注意事项

1. **性能**: 过多的transition会影响性能
2. **时长选择**: 根据场景选择合适的duration
3. **用户体验**: 避免过长的过渡时间
4. **一致性**: 保持相似的过渡时间
5. **实时数据**: 实时数据应禁用transition
6. **浏览器兼容**: 所有现代浏览器都支持


Security scan verdicts — ClawScan: clean | static analysis: clean | VirusTotal: clean | SkillSpector: suspicious (MEDIUM)