# dppmap **Repository Path**: gzlpsdp/dppmap ## Basic Information - **Project Name**: dppmap - **Description**: 封装整合maplibre+jts+geopackage+proj4j+shapefile-core开源地图应用 - **Primary Language**: Android - **License**: AFL-3.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 0 - **Created**: 2026-04-24 - **Last Updated**: 2026-07-23 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README ## GIS Map DPP SDK 使用指南 [![](https://jitpack.io/v/com.gitee.gzlpsdp/dppmap.svg)](https://jitpack.io/#com.gitee.gzlpsdp/dppmap) 本指南旨在帮助 Android 开发者快速、正确地集成并使用 GIS Map DPP SDK。所有功能均附有完整代码示例和关键注意事项,确保您能顺利上手。 ## SDK 能力全景 |能力域 |关键功能| |----|----| 底图加载| MapLibre 矢量/影像底图、天地图 Key 集成、自定义瓦片 URL 坐标系统| WGS84 ↔ GCJ02 ↔ BD09 互转、EPSG:4326/4490/3857 投影支持、DPPGeometry 坐标转换 数据源| GeoPackage (.gpkg)、GeoJSON URL、Shapefile (.shp/.shx/.dbf) Feature CRUD| insert / update / delete / query(支持 ID、字段、矩形、WHERE 查询,含异步/同步版本) 绘制工具| 画点、画线、自由画线、画多边形、自由多边形、撤销/重做 测量工具| 距离测量、面积测量、自定义单位(km/m/ha/ac) 编辑工具| 节点编辑、多边形拆分/合并、Feature 移动 空间运算| 相交(Intersection)、并集(Union)、差集(Difference)、对称差集(SymDifference) 裁剪/分割| Clip(按多边形裁剪 Feature)、Trim(用线段裁剪)、Merge(合并 Feature)、Split(分割) 缓冲区| createBuffer(按米创建缓冲区) 空间分析| Voronoi 图、等值线(Contour)、核密度估计(KDE)、IDW 插值、自然邻域插值、克里金(Kriging)插值 空间关系| contains / within / equals / overlaps / intersects(同步 + 异步回调) 锐角检测| getSharpAngles(检测 ≤30° 锐角顶点) 自相交检测| isSelfIntersecting + getSelfIntersectionPoints 远程服务| WMS、WMTS、WFS 服务查询(GetFeatureInfo) 定位| GPS/指南针定位、实时位置追踪、外部坐标注入 指南针| 内置指南针 / 自定义指南针 UI 气泡| Callout 弹窗、自定义布局、列表样式 图形叠加层| GraphicsOverlay(点/线/面/图标自由绘制) ## 功能特性 ### 🗺️ 地图引擎 - **基于 MapLibre:** 高性能矢量/栅格地图渲染,支持平滑缩放、旋转、倾斜。 - **多类型底图:** 支持天地图(矢量/影像)、自定义瓦片(WMS/WMTS)、空白底图。 - **瓦片缓存:** 可配置磁盘缓存路径与大小(默认 50MB),支持离线浏览已访问区域。 - **多坐标系支持:** 动态配置存储坐标系(EPSG:4326 / 4490 / 3857 等),自动转换显示。 ### 📂 矢量数据管理 - **GeoPackage 加载:** 无缝加载 .gpkg 文件,支持点、线、面图层,自动识别几何类型。 - **Shapefile 加载:** 支持 .shp + .shx + .dbf 加载,可指定字符集(GBK/UTF-8)和源坐标系,支持内存限制与批处理控制。 - **GeoJSON 加载:** 支持在线 URL 或本地文件加载,自动解析并渲染。 - **DataFrame 统一接口:** 所有矢量数据(GeoPackage/GeoJSON/Shapefile)均返回 DataSourceLayer,提供统一的样式、查询、显隐控制。 ### CRUD 操作(FeatureTable) - **插入(Create):** 支持自定义几何对象(点/线/面)和属性字段插入,异步/同步双模式。 - **删除(Delete):** 支持按要素 ID 删除或清空整表。 - **更新(Update):** 支持按要素 ID 更新指定属性字段。 - **查询(Retrieve):** 支持全量查询、按 ID 查询、矩形范围查询、字段等值查询、多条件 AND 查询,参数化查询安全防注入。 ### ✏️ 交互工具 #### 绘制(DrawTool) - ***点:*** 单击地图添加点。 - ***线:*** 点状模式(单击添加顶点,长按完成)和自由绘制模式(滑动绘制,松手完成)。 - ***面:*** 点状模式(单击添加顶点,长按闭合)和自由绘制模式(滑动绘制,松手闭合)。 - ***撤销/重做:*** 支持点状模式下撤销/重做上一次添加的顶点。 #### 测量(MeasureTool) - ***交互式测量:*** 单击添加测量点,实时显示长度(折线)或周长+面积(多边形),结果以地图标注形式固定在几何中心。 - ***编程式测量:*** 直接计算两点距离、折线长度、多边形面积,支持多种单位(米/千米/英里/英尺,平方米/平方公里/公顷/英亩)。 #### 编辑(EditTool) - ***顶点编辑:*** 拖拽移动顶点、单击删除顶点(确认对话框)、在线段上单击添加新顶点。 - ***撤销/重做:*** 支持顶点操作的撤销和重做。 - ***保存/取消:*** 修改后可保存入库或取消丢弃。 #### 移动(MoveTool) - ***整体拖拽:*** 拖拽移动点/线/面要素到新位置,显示临时图形预览,保存/取消确认对话框。 - ***样式继承:*** 临时图形样式与原要素保持一致。 #### 分割(SplitTool) - ***线分割:*** 用一条线将线要素分割成多段。 - ***面分割:*** 用一条贯穿线将面要素分割成多个子面。 - ***异步回调:*** 返回所有子要素列表,由上层自行处理入库。 #### 合并(MergeTool) - ***线合并:*** 将多条线要素合并为一条线(或 MultiLineString)。 - ***面合并:*** 将多个面要素合并为一个面(或 MultiPolygon)。 - ***编程式调用:*** 直接传入要素列表,回调返回合并后的新要素。 ### 🎨 样式系统 #### 静态样式 - ***点样式:*** 自定义颜色、半径(像素)。 - ***线样式:*** 自定义颜色、宽度(像素)。 - ***面样式:*** 自定义填充色、填充透明度、边框色、边框宽度、虚线数组(实线/间隔长度)。 #### 动态样式 - ***点动态样式:*** 根据属性字段值映射点颜色,支持自定义默认颜色和半径。 - ***线动态样式:*** 根据属性字段值映射线颜色,支持自定义默认颜色和宽度。 - ***面动态样式:*** 根据属性字段值映射填充色(边框色可独立映射),支持自定义默认值、边框宽度、虚线、透明度。 - ***清除动态样式:*** 一键恢复静态样式。 #### 文本标注 - ***字段标注:*** 支持点/线/面图层基于属性字段显示文本标注。 - ***样式自定义:*** 文字颜色、大小、偏移、描边颜色/宽度、允许重叠。 - ***线要素中心点标注:*** 线要素可强制在几何中心显示标注(任何缩放级别均可见)。 ### 🔍 数据过滤与查询 - ***表达式过滤:*** 基于 MapLibre Expression,支持等值、比较(>/>=/)。 - ***长按监听:*** 返回点击位置和命中要素列表。 - ***要素查询:*** 自动查询点击位置附近要素(支持容差矩形),去重返回。 #### 要素高亮 - ***单个高亮:*** 支持按要素对象或(表名 + ID)高亮。 - ***批量高亮:*** 支持批量高亮同一表或多个表的要素。 - ***叠加高亮:*** 支持增量添加高亮(保留已有高亮)。 - ***临时高亮:*** 支持高亮内存中的临时要素(不依赖数据库)。 - ***高亮样式:*** 可分别配置点/线/面的高亮颜色、透明度、线宽、点半径。 - ***清除高亮:*** 一键清除所有高亮或临时高亮。 #### 临时图形层(GraphicsOverlay) - ***多图层管理:*** 支持创建多个独立覆盖层(如 "highlight"、"measure"、"temp")。 - ***图形类型:*** 支持点(圆点/图标)、线、面、多线、多面。 - ***样式属性:*** 颜色、半径、线宽、填充色、透明度、虚线。 - ***显隐控制:*** 独立控制每个覆盖层的可见性。 #### 图片标注 - ***图标标注:*** 在地图上添加自定义图标(Drawable 资源),支持缩放。 - ***位置更新:*** 动态更新标注位置。 - ***资源释放:*** 移除标注时自动清理图层、数据源和图片资源。 ### 📐 高级几何运算 #### 裁剪(Clip) - ***裁剪要素:*** 用面几何裁剪点/线/面要素,保留落在裁剪面内部的部分。 - ***自动入库:*** 裁剪成功后原要素被删除,新要素插入同一表。 - ***异步执行:*** 不阻塞 UI,回调返回新要素。 #### 修边(Trim) - ***智能分割:*** 用线切割线/面要素。 - ***自动保留:*** 若产生 2 个子要素,自动保留面积/长度较大的并入库。 - ***多选模式:*** 若产生 ≥3 个子要素,返回列表(未入库),由上层选择保留哪些。 #### 缓冲区(Buffer) - ***缓冲区生成:*** 为点/线/面要素生成指定距离(米)的缓冲区多边形。 - ***不入库:*** 返回 DPPGeometry,便于预览或二次处理。 - ***异步执行:*** 回调返回缓冲几何。 #### 叠加分析(Overlay) - ***相交(Intersection):*** 返回两个要素的公共部分。 - ***合并(Union):*** 返回两个要素的并集。 - ***差集(Difference):*** 返回属于 A 但不属于 B 的部分。 - ***对称差(SymDifference):*** 返回属于 A 或 B 但不同时属于两者的部分。 ### 🔬 几何有效性检查 #### 简单性判断(自相交检测) - ***自相交检测:*** 判断点/线/面/多部件几何是否存在自相交或自我重叠。 - ***自相交点提取:*** 获取所有自相交点坐标列表。 - ***同步/异步:*** 支持同步(工作线程)和异步(UI 线程)两种调用方式。 #### 尖角检测(Sharp Angle Detection) - ***角度阈值检测:*** 检测多边形中内角 ≤ 指定阈值(如 30°)的顶点。 - ***返回尖角点:*** 返回所有尖角顶点坐标(WGS84),可直接用于高亮显示。 #### 空间关系判断 - ***包含(Contains):*** 判断 A 是否完全包含 B。 - ***在内部(Within):*** 判断 A 是否完全在 B 内部。 - ***拓扑相等(Equals):*** 判断两个几何是否拓扑相等。 - ***部分重叠(Overlaps):*** 判断两个同维几何是否部分重叠。 - ***相交(Intersects):*** 判断两个几何是否有公共点。 ### 🧮 空间分析工具 #### 泰森多边形(Voronoi Diagram) - ***空间划分:*** 将平面划分为以输入点为中心的邻近区域。 - ***裁剪控制:*** 支持自定义裁剪矩形,自动外扩 10%。 - ***异步执行:*** 返回 DPPolygon 列表,适用于设施服务区、定性聚类等。 #### 等值线生成(Contour Lines) - ***离散点插值:*** 从离散点数据生成等值线(等高线、等温线等)。 - ***多级提取:*** 支持自定义等值线级别数组。 - ***线段合并:*** 自动合并同级别的共线线段,生成连续多段线。 #### 核密度分析(Kernel Density Estimation) - ***密度表面:*** 估算点数据在空间上的聚集程度。 - ***自动网格:*** 自动生成网格点进行密度计算。 - ***指定网格:*** 支持自定义网格点。 - ***带宽估算:*** 提供 Silverman 规则自动估算带宽。 #### 插值分析 - ***反距离权重(IDW):*** 基于距离加权的快速插值方法,支持自定义幂参数。 - ***自然邻域插值(Natural Neighbor):*** 基于 Delaunay 三角网的平滑插值。 - ***克里金插值(Kriging):*** 基于空间自相关性的最优插值,提供预测值。 ### 📍 定位与传感器 - ***GPS 定位:*** 集成系统定位,支持定位图标样式(普通蓝点/方向箭头/导航大图标)、相机追踪模式(仅跟随/跟随+方向)、脉冲动画、精度圈透明度。 - ***外部定位注入:*** 支持 RTK、蓝牙 GNSS 等外部设备高精度坐标注入,自动显示定位图标和精度圈。 - ***位置回调:*** 实时接收位置更新(主线程回调)。 - ***地图旋转跟随:*** 基于设备旋转向量传感器,自动旋转地图使设备朝向始终指向屏幕上方。 - ***电子罗盘:*** 可结合传感器实现地图方向实时跟随。 ### 🧭 指北针与方向 - ***内置指北针:*** 默认右上角显示,点击回正北。 - ***自定义指北针:*** - 任意 Drawable 图标(矢量图/位图)。 - 任意尺寸(dp)。 - 任意位置(Gravity + 四周边距)。 - 点击切换电子罗盘跟随模式。 - ***一直显示:*** 可配置为始终显示(不随朝向正北淡出)。 ### 🗺️ 地图视口控制 - ***心点设置:*** 无动画/带动画移动地图中心。 - ***缩放控制:*** 设置缩放级别,支持最小/最大缩放限制。 - ***方位角控制:*** 无动画/带动画旋转地图。 - ***缩放到几何/要素:*** 自动计算几何边界,支持自定义边距和动画时长。 - ***获取当前状态:*** 获取当前中心点、缩放级别。 ### 🏷️ 数据格式转换 #### 几何数据解析(DppGeometryParser) - ***Esri JSON 解析:*** 支持点({"x": lon, "y": lat})、线({"paths": [...]})、面({"rings": [...]})格式。 - ***双向转换:*** DPPGeometry ↔ Esri JSON。 - ***坐标系转换:*** 支持任意坐标系互转(如 CGCS2000 → WGS84)。 - ***全局默认配置:*** 可设置默认源/目标坐标系。 #### 坐标转换(CoordinateConverter) - ***WGS84 ↔ GCJ02:*** 国测局坐标互转。 - ***GCJ02 ↔ BD09:*** 百度坐标互转。 - ***WGS84 ↔ BD09:*** 直接转换(两步完成)。 - ***任意 EPSG 转换:*** 支持 EPSG:4326 / 4490 / 3857 互转,可扩展其他坐标系。 ### 🌐 网络服务集成 #### WMS 服务 - ***动态瓦片:*** 添加 WMS 栅格图层(支持自定义 SRS、图片格式、透明、瓦片大小)。 - ***GetFeatureInfo:*** 点击地图查询 WMS 要素属性,返回 DPPFeature 列表。 #### WMTS 服务 - ***预切瓦片:*** 添加 WMTS 栅格图层(支持自定义格式、瓦片矩阵集)。 - ***图层控制:*** 独立控制 WMTS 图层的显隐。 #### WFS 服务 - ***矢量查询:*** 点击地图查询 WFS 要素,支持空间过滤(DWITHIN)和属性过滤(CQL_FILTER)。 - ***GeoJSON 解析:*** 自动解析 WFS 返回的 GeoJSON 为 DPPFeature 列表。 ### 🧩 扩展与集成 #### 第三方库集成 - ***MapLibre:*** 地图渲染引擎。 - ***GeoPackage:*** 矢量数据存储与查询。 - ***JTS:*** 几何运算库(裁剪、缓冲、叠加、空间关系判断等)。 - ***proj4j:*** 坐标转换库。 - ***Smile:*** 插值分析(克里金、核密度、IDW 等)。 - ***Tinfour:*** 自然邻域插值。 ## 快速开始 **1. 添加依赖** ***Step 1.*** 在项目根目录的 build.gradle 中添加 JitPack 仓库: ```groovy allprojects { repositories { maven { url 'https://jitpack.io' } } } ``` 在最新版本android studio中增加settings.gradle ```groovy dependencyResolutionManagement { repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) repositories { google() mavenCentral() maven { url 'https://jitpack.io' } } } ``` ***Step 2.*** 在 app 模块的 build.gradle 中添加依赖: ```groovy dependencies { implementation 'com.gitee.gzlpsdp:dppmap:latest_version' // 替换为最新版本 } ``` ## 权限配置 在 AndroidManifest.xml 中添加存储权限(用于读取 GeoPackage 文件): ```xml ``` ## 开始使用 在 Activity 布局中添加 GisMapView2D ```xml ``` **初始化地图** ```java public class MainActivity extends AppCompatActivity { private GisMapView2D gisMapView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); gisMapView = findViewById(R.id.gisMapView); // 配置地图选项 GisMapOptions options = new GisMapOptions.Builder() // ---------- 底图配置 ---------- .setBaseMapType(GisMapOptions.BaseMapType.TIANDITU_VECTOR) // 天地图矢量 .setTianDiTuKey("your_tianditu_key") // 必填 // ---------- 初始视口 ---------- .setCenter(26.9, 106.7) // 纬度, 经度 .setZoom(10.0f) // 缩放级别 (通常 3~18) .setMinZoom(3.0) .setMaxZoom(18.0) // ---------- 手势控制 ---------- .setEnableRotateGesture(true) // 允许双指旋转 .setEnableTiltGesture(false) // 禁止倾斜 // ---------- 指北针 ---------- .setCompassEnabled(true) .setCompassFadeWhenFacingNorth(false) // 一直显示 .setCompassGravity(Gravity.TOP | Gravity.END) .setCompassMargins(16, 16, 16, 16) .setCompassImage(R.drawable.my_compass) // 可选自定义图标 // ---------- 瓦片缓存 ---------- .setCacheEnabled(true) .setCachePath(getExternalFilesDir("map_cache").getAbsolutePath()) .setCacheSizeMB(100) // 最大 100 MB .build(); gisMapView.setOptions(options); gisMapView.setCallback(() -> { // 地图样式加载完成,可以开始加载数据、添加图层、启用交互工具等 loadGeoPackage(); }); } } ``` >**注意:** onMapReady 是 SDK 所有后续操作的起点。在回调之前调用的大部分方法会因地图未就绪而无效(但不会崩溃,会被暂存)。 ## 核心功能 **在地图加载完成后执行** ``` //在onMapReady中加载gpkg确保地图初始化完成 getGisMapView().setCallback(new GisMapCallback() { @Override public void onMapReady() { } }); ``` ### 坐标系设置(重要) SDK 内部以 **WGS84 (EPSG:4326)** 作为显示和交互坐标系,所有输入/输出的经纬度均基于 WGS84。存储坐标系可配置,以适应不同数据源(如 CGCS2000)。 **必须在加载任何数据之前设置存储坐标系,否则坐标会错误。** ```java // 在 Application 或 Activity 初始化时设置(通常在 setOptions 之前或之后,但必须在 loadGeoPackage 之前) GisMapView2D.setGlobalStorageCrs("EPSG:4490"); // 假设数据存储为 CGCS2000 // 获取当前设置 String crs = GisMapView2D.getGlobalStorageCrs(); // 判断是否需要转换 boolean need = GisMapView2D.isCrsTransformNeeded(); ``` >支持的值:"EPSG:4326", "EPSG:4490", "EPSG:3857" 等。 ### 地图配置选项(GisMapOptions) `GisMapOptions` 用于在地图初始化时配置地图的初始状态、底图类型、手势、指北针等。通过 Builder 模式链式调用。 **参数总览** |方法| 说明| 默认值| |----|-----|-----| ***底图相关*** |`setBaseMapType(BaseMapType)`| 底图类型:NONE, TIANDITU_VECTOR, TIANDITU_IMAGE, CUSTOM| NONE |`setTianDiTuKey(String)`| 天地图密钥(仅当底图为天地图时有效) |"" |`setCustomTileUrl(String)`| 自定义瓦片 URL 模板(仅当底图为 CUSTOM 时有效)| "" ***地图视口*** |`setCenter(LatLng) / setCenter(lat, lon)`| 初始中心点(纬度, 经度) |(39.9, 116.4) |`setZoom(float)`| 初始缩放级别(通常 3~18) |7.0f |`setMinZoom(double)`| 最小缩放级别 |3.0 |`setMaxZoom(double)`| 最大缩放级别 |18.0 ***手势控制*** |`setEnableRotateGesture(boolean)`| 是否允许双指旋转地图手势| false |`setEnableTiltGesture(boolean)`| 是否允许双指倾斜地图手势 |false ***指北针*** |`setCompassEnabled(boolean)` |是否启用指北针 |true |`setCompassFadeWhenFacingNorth(boolean)` |false = 一直显示;true = 仅在非正北时显示(默认行为)| false |`setCompassGravity(int)`| 指北针位置(Gravity 常量组合,如 Gravity.TOP | Gravity.END)| Gravity.TOP | Gravity.END |`setCompassMargins(int left, int top, int right, int bottom)`| 指北针到地图四边的距离(像素)| (0,0,0,0) |`setCompassImage(int drawableRes)`| 自定义指北针图标资源 ID(0 表示使用默认图标) |0 **使用示例** ```java GisMapOptions options = new GisMapOptions.Builder() // ---------- 底图配置 ---------- .setBaseMapType(GisMapOptions.BaseMapType.TIANDITU_VECTOR) // 天地图矢量 .setTianDiTuKey("your_tianditu_key") // 必填 // ---------- 初始视口 ---------- .setCenter(26.9, 106.7) // 纬度, 经度 .setZoom(10.0f) // 缩放级别 (通常 3~18) .setMinZoom(3.0) .setMaxZoom(18.0) // ---------- 手势控制 ---------- .setEnableRotateGesture(true) // 允许双指旋转 .setEnableTiltGesture(false) // 禁止倾斜 // ---------- 指北针 ---------- .setCompassEnabled(true) .setCompassFadeWhenFacingNorth(false) // 一直显示 .setCompassGravity(Gravity.TOP | Gravity.END) .setCompassMargins(16, 16, 16, 16) .setCompassImage(R.drawable.my_compass) // 可选自定义图标 // ---------- 瓦片缓存 ---------- .setCacheEnabled(true) .setCachePath(getExternalFilesDir("map_cache").getAbsolutePath()) .setCacheSizeMB(100) // 最大 100 MB .build(); gisMapView.setOptions(options); ``` **详细说明** ***1. 底图类型(BaseMapType)*** |枚举值 |说明| |-----|----| |NONE |无底图(透明背景) |TIANDITU_VECTOR| 天地图矢量底图(需要密钥) |TIANDITU_IMAGE |天地图影像底图(需要密钥) |CUSTOM| 自定义瓦片服务(需要提供 URL 模板) 自定义瓦片 URL 模板示例: ```java .setCustomTileUrl("https://tile.example.com/{z}/{x}/{y}.png") ``` ***2. 指北针常驻与自定义图标*** - 默认情况下,指北针只在用户旋转地图后短暂出现,点击复位后自动消失。 - 设置 setCompassFadeWhenFacingNorth(false) 可使其一直显示,无论地图是否朝北。 - 自定义图标建议使用 24dp~32dp 的矢量图或 PNG,过大可能影响点击区域。 ***3. 手势启用建议*** - 若需要用户自由旋转地图,请设置 setEnableRotateGesture(true)。 - 倾斜手势(setEnableTiltGesture)允许双指上下滑动改变俯仰角,默认禁用。 - 平移和缩放手势始终可用,无需配置。 ***4. 注意事项*** - 天地图密钥需自行申请(天地图官网)。 - 自定义瓦片 URL 需包含 {z}, {x}, {y} 占位符。 - 指北针的 compassMargins 单位为像素,建议根据屏幕密度转换(如 dp2px)。 - 所有配置仅在地图初始化前有效;地图加载后可通过 GisMapView2D 的动态方法修改(如 setCompassEnabled、setBearing 等)。 ### 底图控制 **3.1 天地图** ```java // 在 Options 中配置即可自动加载 options.setBaseMapType(GisMapOptions.BaseMapType.TIANDITU_VECTOR) .setTianDiTuKey("your_key"); ``` **3.2 自定义瓦片(WMS / WMTS)** ***WMS 图层:*** ```java String wmsId = gisMapView.addWmsLayer( "http://your_server/geoserver/wms", "layer_name", "EPSG:3857", "image/png" ); gisMapView.setWmsLayerVisible(wmsId, true); ``` ***WMTS 图层:*** ```java gisMapView.addWmtsLayer( "http://your_server/wmts", "layer_name", "wmts_source", "wmts_layer", "image/png", "EPSG:3857" ); ``` ### 瓦片缓存配置 在 GisMapOptions 中配置缓存: ```java GisMapOptions options = new GisMapOptions.Builder() .setBaseMapType(GisMapOptions.BaseMapType.TIANDITU_VECTOR) .setTianDiTuKey("your_key") // 开启缓存 .setCacheEnabled(true) // 设置缓存路径(外部存储,需权限) .setCachePath(getExternalFilesDir("map_cache").getAbsolutePath()) // 设置缓存大小(MB),默认 50MB .setCacheSizeMB(100) .build(); ``` 运行时控制缓存: ```java // 启用/禁用 gisMapView.setTileCacheEnabled(true); // 设置缓存大小(MB) gisMapView.setTileCacheSize(200); // 清空缓存 gisMapView.clearTileCache(); // 查看缓存大小 String size = gisMapView.getTileCacheSizeFormatted(); ``` ### 加载 GeoPackage ```java private void loadGeoPackage() { File gpkgFile = new File(getExternalFilesDir(null), "sample.gpkg"); // 如果文件不存在则从 assets 复制(略) gisMapView.loadGeoPackage(gpkgFile.getAbsolutePath(), new GeoPackageCallback() { @Override public void onSuccess(List tableNames) { Toast.makeText(MainActivity.this, "加载成功,表:" + tableNames, Toast.LENGTH_SHORT).show(); // 获取各矢量表 FeatureTable pointTable = gisMapView.getFeatureTable("points"); FeatureTable lineTable = gisMapView.getFeatureTable("lines"); FeatureTable polygonTable = gisMapView.getFeatureTable("polygons"); } @Override public void onError(String error) { Toast.makeText(MainActivity.this, "加载失败:" + error, Toast.LENGTH_LONG).show(); } }); } ``` ### 获取几何操作类FeatureTable 上述加载完geopackage后即可通过getFeatureTable方法获取,里面也可以执行各种CURD操作 ```java // 获取各矢量表 FeatureTable pointTable = gisMapView.getFeatureTable("points"); FeatureTable lineTable = gisMapView.getFeatureTable("lines"); FeatureTable polygonTable = gisMapView.getFeatureTable("polygons"); ``` **一、基本信息** |方法 |返回类型 |说明| |----|---|----| getName()| String| 获取表名 getGeometryType()| String |获取几何类型(如 "POINT"、"LINESTRING"、"POLYGON") getFeatureCount() |int |获取要素总数 **二、数据操作(CRUD)** ***插入(Create)*** |方法| 说明| |----|----| `insert(Map attributes, DPPGeometry geometry, InsertCallback callback)` |异步插入要素,回调返回新要素 ID `insertSync(Map attributes, DPPGeometry geometry)` |同步插入要素,返回新要素 ID(可能抛出异常) ```java Map attrs = new HashMap<>(); attrs.put("name", "北京"); attrs.put("population", 2154); DPPPoint point = new DPPPoint(116.4, 39.9); pointTable.insert(attrs, point, new InsertCallback() { @Override public void onSuccess(long featureId) { Log.d("Insert", "插入成功,ID: " + featureId); } @Override public void onError(String error) { Log.e("Insert", "插入失败: " + error); } }); ``` ***删除(Delete)*** |方法| 说明| |----|----| `deleteFeature(long featureId, OperationCallback callback)` |异步删除指定要素 `deleteAll(OperationCallback callback)` |异步删除所有要素 `deleteFeatureSync(long featureId)` |同步删除指定要素 `deleteAllSync()` |同步删除所有要素 ***更新(Update)*** |方法| 说明| |----|----| `update(long featureId, Map attributes, OperationCallback callback)` |异步更新指定要素的属性 `updateSync(long featureId, Map attributes)` |同步更新指定要素的属性 ```java Map updates = new HashMap<>(); updates.put("name", "北京市"); pointTable.update(123L, updates, new OperationCallback() { @Override public void onSuccess() { Log.d("Update", "更新成功"); } @Override public void onError(String error) { Log.e("Update", "更新失败: " + error); } }); ``` ***查询(Retrieve)- 异步*** |方法| 说明| |----|----| `queryAll(QueryCallback callback)` |查询所有要素 `queryById(long featureId, QueryCallback callback)` |按 ID 查询要素(安全,参数化) `queryByRect(GisPoint bottomLeft, GisPoint topRight, QueryCallback callback)` |按矩形范围查询要素(经纬度) `queryByField(String fieldName, Object value, QueryCallback callback)` |按字段等值查询 `queryByFields(Map conditions, QueryCallback callback)` |按多个字段等值查询(AND 关系) `queryByWhere(String whereClause, QueryCallback callback)` |自定义 WHERE 条件查询(注意 SQL 注入风险) ***查询(Retrieve)- 同步*** |方法 |返回类型 |说明 |----|----|----| `queryAllSync()`| `List` |查询所有要素 `queryByIdSync(long featureId)` |DPPFeature |按 ID 查询要素 `queryByRectSync(GisPoint bottomLeft, GisPoint topRight)` |`List` |按矩形范围查询要素 `queryByFieldSync(String fieldName, Object value)` |`List` |按字段等值查询 `queryByFieldsSync(Map conditions)` |`List` |按多个字段等值查询 `queryByWhereSync(String whereClause)`| `List` |自定义 WHERE 条件查询 >⚠️ **重要:** ***queryByWhere 和 queryByWhereSync 已被标记为 @Deprecated,因为它们存在 SQL 注入风险,不建议在新代码中使用。请改用 queryByField、queryByFields 等安全方法,这些方法内部使用参数化查询,完全避免注入风险。*** ```java // 异步查询 pointTable.queryByField("name", "北京", new FeatureTable.QueryCallback() { @Override public void onSuccess(List features) { for (DPPFeature f : features) { Log.d("Query", "找到: " + f.getProperty("name")); } } @Override public void onError(String error) { Log.e("Query", "查询失败: " + error); } }); // 同步查询(可在子线程执行) try { List features = pointTable.queryAllSync(); DPPFeature feature = pointTable.queryByIdSync(123L); } catch (Exception e) { e.printStackTrace(); } ``` **三、查询回调接口*** ```java interface QueryCallback { void onSuccess(List features); void onError(String error); } ``` **四、样式控制** ***静态样式*** |方法| 说明| |----|----| `setPointStyle(int color, float radius)` |设置点样式(颜色 + 半径) `setLineStyle(int color, float width)`| 设置线样式(颜色 + 宽度) `setPolygonStyle(int fillColor, int strokeColor, float strokeWidth, Float[] dashArray)` |设置面样式(填充色 + 边框色 + 宽度 + 虚线) `setFillOpacity(float opacity)` |设置面填充透明度(0~1) `resetStyle()`| 恢复默认样式 ***动态样式(按属性值映射颜色)*** |方法| 说明| |----|----| `setDynamicPointStyle(String fieldName, Map valueColorMap, int defaultColor, Float radius)` |动态点样式 `setDynamicLineStyle(String fieldName, Map valueColorMap, int defaultColor, float defaultWidth)` |动态线样式 `setDynamicPolygonStyle(...)` |动态面样式(支持填充/边框分别映射) `clearDynamicStyle()` |清除动态样式,恢复静态样式 ```java // 按 "type" 字段映射颜色 Map colorMap = new HashMap<>(); colorMap.put("road", Color.RED); colorMap.put("river", Color.BLUE); lineTable.setDynamicLineStyle("type", colorMap, Color.GRAY, 3f); ``` ***获取当前样式*** |方法 |返回类型 |说明 |----|----|----| `getPointColor()` |int |获取点颜色 `getPointRadius()` |float| 获取点半径 `getLineColor()` |int |获取线颜色 `getLineWidth()` |float |获取线宽度 `getFillColor()` |int |获取面填充色 `getFillOpacity()` |float |获取面填充透明度 `getStrokeColor()` |int |获取面边框色 `getStrokeWidth()` |float |获取面边框宽度 `getDashArray()` |Float[] |获取虚线数组 **五、数据过滤** |方法| 说明| |----|----| `setFilter(Expression filterExpression)` |设置过滤条件(MapLibre Expression),传 null 清除 `clearFilter()` |清除过滤条件,显示所有要素 ```java // 仅显示 name = "北京" 的要素 pointTable.setFilter(Expression.eq(Expression.get("name"), "北京")); // 清除过滤 pointTable.clearFilter(); ``` **六、标注样式** |方法| 说明| |----|----| `setLabelStyle(LabelStyle style)` |设置标注样式(字段名、颜色、大小等),传 null 关闭标注 ```java LabelStyle style = new LabelStyle.Builder() .setFieldName("name") .setTextColor(Color.BLACK) .setTextSize(14f) .setHaloColor(Color.WHITE) .setHaloWidth(2f) .build(); pointTable.setLabelStyle(style); ``` **七、编辑模式** |方法| 说明| |----|----| `startEdit(long featureId)` |进入要素编辑模式(需配合 EditTool 使用) `stopEdit()` |退出编辑模式 ```java // 在 GisMapView2D 中更推荐使用 gisMapView.startEdit(feature); gisMapView.saveCurrentEdit(); gisMapView.cancelCurrentEdit(); ``` **八、可见性控制** |方法| 说明| |----|----| `setVisible(boolean visible)` |显示/隐藏图层 `isVisible()` |获取可见性状态 **注意事项** - 同步方法(*Sync)会抛出异常,建议在子线程中调用,避免阻塞主线程。 - 异步方法通过回调返回结果,回调在主线程执行,可直接更新 UI。 - SQL 注入风险:queryByWhere 方法直接拼接 SQL,请勿直接拼接用户输入,建议使用参数化查询或预编译方式。 - 样式设置:动态样式会覆盖静态样式,调用 clearDynamicStyle() 可恢复。 ### Shapefile / GeoJSON 加载 加载在线 GeoJSON 或本地 Shapefile 文件。所有新数据源均返回 DataSourceLayer 实例,支持样式设置、查询、可见性控制等操作。 **1. 加载在线 GeoJSON** ```java private void loadGeoJSON() { // 在线 URL 或本地文件路径 String geoJsonSource = "https://example.com/data.geojson"; gisMapView.loadGeoJSON(geoJsonSource, new LoadCallback() { @Override public void onSuccess(String layerId) { Toast.makeText(MainActivity.this, "GeoJSON 加载成功,图层ID:" + layerId, Toast.LENGTH_SHORT).show(); // 获取数据源图层 DataSourceLayer layer = gisMapView.getDataSourceLayer(layerId); if (layer == null) return; // 设置样式(根据几何类型自动生效,若数据中不包含该类型则无效果) layer.setPointStyle(Color.RED, 12f); // 点 layer.setLineStyle(Color.BLUE, 3f); // 线 layer.setPolygonStyle(Color.YELLOW, Color.BLACK, 2f, null); // 面 // 异步查询全部要素 layer.queryAll(new FeatureTable.QueryCallback() { @Override public void onSuccess(List features) { Log.d("GeoJSON", "要素数量:" + features.size()); // 处理要素列表 } @Override public void onError(String error) { Log.e("GeoJSON", "查询失败:" + error); } }); // 同步查询(可能在主线程,注意性能) try { List all = layer.queryAllSync(); } catch (Exception e) { e.printStackTrace(); } } @Override public void onError(String error) { Toast.makeText(MainActivity.this, "GeoJSON 加载失败:" + error, Toast.LENGTH_LONG).show(); } }); } ``` **2. Shapefile 数据加载** SDK 支持从本地文件系统加载 ESRI Shapefile(.shp)格式数据,并自动渲染为地图图层。加载后可通过 DataSourceLayer 接口进行样式设置、查询和图层管理。 SDK 支持读取 .shp + .shx + .dbf,自动处理坐标系转换(若提供 .prj 则可自动识别)。 ```java // 最简单方式(默认编码 ISO-8859-1,源坐标系 WGS84) mapView.loadShapefile("/sdcard/roads.shp", callback); // 指定字符集(中文用 "GBK") mapView.loadShapefileCharset("/sdcard/buildings.shp", "GBK", callback); // 指定源坐标系(如 CGCS2000) mapView.loadShapefileSourceCRS("/sdcard/roads.shp", "EPSG:4490", callback); // 完整参数 mapView.loadShapefile("/sdcard/roads.shp", "GBK", "EPSG:4490", callback); ``` ***方法列表*** |方法| 参数说明| 说明| |----|----|----| |`loadShapefile(String shpPath, LoadCallback callback)` |`shpPath`:.shp 文件路径
`callback`:加载回调 |使用默认字符集(ISO-8859-1)和默认坐标系(WGS84)加载。| `loadShapefileCharset(String shpPath, String charset, LoadCallback callback)`| `charset`:DBF 文件字符集编码(如 `"GBK"`、`"UTF-8"`) |指定字符集,坐标系默认 WGS84。 `loadShapefileSourceCRS(String shpPath, String sourceCRS, LoadCallback callback)` |sourceCRS:源坐标系 EPSG 代码(如 "EPSG:4490") |指定坐标系,字符集默认 ISO-8859-1。 `loadShapefile(String shpPath, String charset, String sourceCRS, LoadCallback callback)` |完整参数 |同时指定字符集和源坐标系。 >提示:loadShapefileSourceCRS 和 loadShapefileCharset 是便捷方法,若您需要同时指定字符集和坐标系,直接使用四参数方法即可。 ***内存控制参数*** (可选,建议在加载大数据前设置) GisMapView2D 提供了两个全局设置方法,用于控制 Shapefile 加载时的内存占用: 方法 |默认值 |说明 |----|----|----| `setShapefileMaxFeatures(int)` |5000 |限制最多加载的要素数量,超过该数量将停止读取。 `setShapefileBatchSize(int)` |500 |批处理大小,影响内存峰值。 这些设置会影响后续所有 loadShapefile 调用,直到再次修改。 ***使用示例*** ```java // 1. 最简单的加载(默认编码、默认坐标系) gisMapView.loadShapefile("/sdcard/roads.shp", new LoadCallback() { @Override public void onSuccess(String layerId) { // 获取图层并设置样式 DataSourceLayer layer = gisMapView.getDataSourceLayer(layerId); if (layer != null) { layer.setLineStyle(Color.GREEN, 4f); layer.setVisible(true); } } @Override public void onError(String error) { Toast.makeText(context, "加载失败: " + error, Toast.LENGTH_LONG).show(); } }); // 2. 指定字符集(中文数据通常使用 "GBK") gisMapView.loadShapefileCharset("/sdcard/buildings.shp", "GBK", new LoadCallback() { @Override public void onSuccess(String layerId) { DataSourceLayer layer = gisMapView.getDataSourceLayer(layerId); if (layer != null) { // 面数据设置样式 layer.setPolygonStyle( Color.parseColor("#80FF5722"), Color.parseColor("#FF5722"), 3f, null ); layer.setVisible(true); } } @Override public void onError(String error) { // 错误处理 } }); // 3. 指定坐标系(例如 CGCS2000) gisMapView.loadShapefileSourceCRS("/sdcard/roads.shp", "EPSG:4490", new LoadCallback() { @Override public void onSuccess(String layerId) { // 处理... } @Override public void onError(String error) { // 错误处理 } }); // 4. 同时指定字符集和坐标系 gisMapView.loadShapefile("/sdcard/roads.shp", "GBK", "EPSG:4490", new LoadCallback() { @Override public void onSuccess(String layerId) { // 处理... } @Override public void onError(String error) { // 错误处理 } }); // 5. 调整内存参数(在加载前调用) gisMapView.setShapefileMaxFeatures(10000); // 最多加载 1 万个要素 gisMapView.setShapefileBatchSize(1000); // 每批处理 1000 个 gisMapView.loadShapefile("/sdcard/bigdata.shp", new LoadCallback() { ... }); ``` ***注意事项*** - 文件完整性:必须提供 .shp、.shx、.dbf 三个文件,且文件名一致(不含扩展名)。.prj 文件可选,但建议提供以自动识别坐标系(若提供,SDK 会自动解析坐标系,无需手动指定 sourceCRS)。 - 字符编码:DBF 文件属性字段可能包含非 ASCII 字符。若属性值出现乱码,请根据文件实际编码设置 charset 参数。常见编码: - 国内中文数据:"GBK" 或 "GB2312" - 国际化数据:"UTF-8" - 默认:"ISO-8859-1" - 坐标系:若数据不是 WGS84,必须通过 sourceCRS 指定源坐标系,SDK 会自动转换到 WGS84 以便在地图上正确显示。常用 EPSG 代码: - EPSG:4326 — WGS84(默认) - EPSG:4490 — CGCS2000 - EPSG:3857 — Web Mercator - 内存管理:超大 Shapefile(数百 MB)可能导致内存溢出。建议设置合理的 maxFeatures 限制加载数量,并适当调整 batchSize 以控制内存峰值。 - 资源释放:使用完毕后,可通过 gisMapView.removeDataSourceLayer(layerId) 移除图层并释放资源。 - 坐标系自动检测:若文件包含 .prj,SDK 会尝试读取并自动识别坐标系,此时无需手动指定 sourceCRS。若未提供 .prj 或识别失败,则需手动指定。 - 重载方法说明:loadShapefileCharset 和 loadShapefileSourceCRS 是专为单一参数设计的便捷方法,您也可以直接使用四参数方法并传入 null 来省略参数。 **3. 通过 DataSourceLayer 提供的通用操作** |方法 |说明| |----|----| setPointStyle(int color, float radius)| 设置点样式(仅当图层包含点要素时生效) setLineStyle(int color, float width) |设置线样式 setPolygonStyle(int fill, int stroke, float width, Float[] dash) |设置面样式(填充色、边框色、宽度、虚线) queryAll(FeatureTable.QueryCallback callback) |异步查询全部要素 queryAllSync() |同步查询全部要素(返回 List) queryByRectSync(GisPoint bl, GisPoint tr) |按矩形范围同步查询 queryByFieldSync(String field, Object value) |按字段值同步查询 setVisible(boolean visible)| 显示/隐藏图层 clear() |释放资源(移除图层和源) **4. 移除图层(释放资源)** ```java gisMapView.removeDataSourceLayer(layerId); ``` **5. 与 GeoPackage 混合使用** ```java // 先加载 GeoPackage gisMapView.loadGeoPackage(...); // 再加载 GeoJSON 作为叠加图层 gisMapView.loadGeoJSON("https://example.com/points.geojson", new LoadCallback() { @Override public void onSuccess(String layerId) { DataSourceLayer layer = gisMapView.getDataSourceLayer(layerId); layer.setPointStyle(Color.MAGENTA, 15f); } @Override public void onError(String error) { // 处理错误 } }); ``` > 所有图层(GeoPackage、GeoJSON、Shapefile)均在地图上共存,且可通过各自的 layerId 独立控制样式和可见性。查询操作(如 queryFeaturesAtPoint)会返回所有可见图层的要素。 ### 几何数据解析与转换工具 DppGeometryParser SDK 提供了 DppGeometryParser 工具类,用于在 Esri 几何格式(点、线、面)与 SDK 内部几何模型(DPPGeometry)之间进行双向转换,并支持任意坐标系的自动转换。 **典型使用场景** - 从后台接口接收几何数据(如 {"rings":[...]}、{"x": lon, "y": lat})并显示在地图上。 - 将用户绘制的几何对象编码为后台要求的 Esri 几何 JSON 格式并提交。 - 在不同坐标系之间转换几何数据(如 CGCS2000 ↔ WGS84)。 **一、方法说明** |方法 |说明| |----|----| |parse(String geoJson) |将几何 JSON 字符串解析为 DPPGeometry,使用默认坐标系(4490 → 4326) |parse(String geoJson, String sourceCRS, String targetCRS) |指定源/目标坐标系解析几何 JSON |toGeoJsonGeometry(DPPGeometry geometry) |将 DPPGeometry 转为 Esri 几何 JSON 字符串,使用默认坐标系(4326 → 4490) |toGeoJsonGeometry(DPPGeometry geometry, String sourceCRS, String targetCRS) |指定源/目标坐标系生成几何 JSON |setDefaultSourceCRS(String crs) |设置默认源坐标系(默认 EPSG:4490) |setDefaultTargetCRS(String crs) |设置默认目标坐标系(默认 EPSG:4326) >**坐标系格式:** 使用 EPSG 代码,如 "EPSG:4326"(WGS84)、"EPSG:4490"(CGCS2000)、"EPSG:3857"(Web Mercator)。 **二、使用示例** ***1. 解析后台返回的几何数据(显示到地图)*** ```java // 后台返回的面几何(CGCS2000 坐标) String geomJson = "{\"rings\":[[[107.811879789,27.912472279],...]]}"; // 使用默认坐标系:4490 → 4326 DPPGeometry geometry = DppGeometryParser.parse(geomJson); if (geometry != null) { // 创建临时要素并显示到地图 Map attrs = new HashMap<>(); attrs.put("name", "烤烟种植地块"); DPPFeature feature = new DPPFeature(-1L, "temp", attrs, geometry); gisMapView.showTemporaryGeometry(geometry, attrs); // 或使用高亮显示 gisMapView.highlightTemporaryFeature(feature); } ``` ***2. 指定坐标系解析*** ```java // 如果数据是 Web Mercator(EPSG:3857),要转到 WGS84 DPPGeometry geometry = DppGeometryParser.parse(geomJson, "EPSG:3857", "EPSG:4326"); ``` ***3. 将用户绘制的几何提交到后台** ```java // 获取绘制结果(WGS84 坐标) DrawResult result = gisMapView.getLastDrawResult(); DPPGeometry geometry = result.getGeometry(); // 转为 Esri 几何 JSON(CGCS2000) String esriJson = DppGeometryParser.toGeoJsonGeometry(geometry); // 或指定目标坐标系 String esriJson2 = DppGeometryParser.toGeoJsonGeometry(geometry, "EPSG:4326", "EPSG:4490"); // 发送到后台 // networkService.submitGeometry(esriJson); ``` ***4. 修改全局默认坐标系*** ```java // 如果后台统一使用 WGS84,可修改默认值 DppGeometryParser.setDefaultSourceCRS("EPSG:4326"); DppGeometryParser.setDefaultTargetCRS("EPSG:4326"); // 后续调用无需再传 CRS 参数 DPPGeometry geom = DppGeometryParser.parse(geoJson); String esriJson = DppGeometryParser.toGeoJsonGeometry(geom); ``` **三、支持的几何格式** |几何类型| Esri JSON 格式 |说明 |----|----|----| 点| {"x": 106.7, "y": 26.5} |单点坐标 线 |{"paths": [[[x1,y1], [x2,y2], ...]]} |单条或多条路径 面 |{"rings": [[[x1,y1], ...], [内环...]]} |外环 + 可选内环 **四、注意事项** - ***坐标系默认值:*** 默认源坐标系为 EPSG:4490(CGCS2000),目标为 EPSG:4326(WGS84),与常见后台和地图显示需求匹配。 - ***坐标转换:*** 内部使用 CoordinateConverter.transformPoint,需确保已配置对应的坐标系支持(Proj4J)。 - ***容错处理:*** 解析失败时返回 null,调用时建议判空。 - ***多部件支持:*** 当前支持点、线、多边形(含内环),更多类型可按需扩展。 - ***属性独立:*** 此工具仅处理几何数据,属性字段(如 specklesAttribute)需另行解析。 **五、完整示例(网络请求 + 解析 + 显示)** ```java // 1. 从后台获取数据 String apiUrl = "https://your-api.com/query"; NetworkUtils.executeGet(apiUrl, new RawQueryCallback() { @Override public void onSuccess(String response) { // 2. 解析响应 JsonObject root = JsonParser.parseString(response).getAsJsonObject(); JsonArray dataArray = root.getAsJsonArray("data"); for (int i = 0; i < dataArray.size(); i++) { JsonObject item = dataArray.get(i).getAsJsonObject(); // 3. 解析几何(默认 4490 → 4326) String geomJson = item.get("geom").getAsString(); DPPGeometry geometry = DppGeometryParser.parse(geomJson); if (geometry == null) continue; // 4. 解析属性(specklesAttribute 是 JSON 字符串) String attrJson = item.get("specklesAttribute").getAsString(); Map attrs = parseAttributes(attrJson); attrs.put("name", item.get("specklesName").getAsString()); // 5. 显示到地图 Graphic graphic = new Graphic(geometry, attrs); graphic.setAttribute("fillColor", "#80FF5722"); graphic.setAttribute("strokeColor", "#FF5722"); graphic.setAttribute("strokeWidth", 3f); gisMapView.getGraphicsOverlay("remote_data").addGraphic(graphic); } } @Override public void onError(String error) { // 错误处理 } }); public static Map parse(String attrJson) { Map map = new HashMap<>(); if (attrJson == null || attrJson.isEmpty()) return map; try { JsonObject obj = JsonParser.parseString(attrJson).getAsJsonObject(); for (String key : obj.keySet()) { map.put(key, obj.get(key).getAsString()); } } catch (Exception e) { // 解析失败返回空 } return map; } ``` ### 绘制图形 支持点、线、面两种绘制模式:点状(单击添加点/顶点,长按完成)和 自由绘制(滑动绘制,松手完成)。 |功能| 方法| |----|----| |绘制点 |`gisMapView.startDrawPoint()` |点状线 |`gisMapView.startDrawLineByPoint()` |自由线 |`gisMapView.startDrawFreehandLine()` |点状面 |`gisMapView.startDrawPolygonByPoint()` |自由面 |`gisMapView.startDrawFreehandPolygon()` |退出绘制 |`gisMapView.stopDraw()` |完成并获取结果 |`gisMapView.finishDraw() + getLastDrawResult()` |撤销上一次添加的顶点(仅在点状绘制模式下有效)|`gisMapView.undoDraw()` |重做上一次撤销的顶点|`gisMapView.redoDraw()` ***绘制后保存示例:*** ```java DrawResult result = gisMapView.getLastDrawResult(); if (result == null) { Toast.makeText(this, "未绘制图形", Toast.LENGTH_SHORT).show(); return; } Map attrs = new HashMap<>(); attrs.put("name", "dengpp新鲜"); attrs.put("upload", 2); String tableName; if(type==1){ tableName = "points"; }else if(type==2){ tableName = "lines"; }else{ tableName = "polygons"; } // 直接使用自定义几何对象插入 gisMapView.insertFeature("lines", attrs, result.getGeometry(), new InsertCallback() { @Override public void onSuccess(long featureId) { runOnUiThread(() -> { Toast.makeText(MainActivity.this, "保存成功,ID=" + featureId, Toast.LENGTH_SHORT).show(); }); } @Override public void onError(String error) { runOnUiThread(() -> { Toast.makeText(MainActivity.this, "保存失败: " + error, Toast.LENGTH_SHORT).show(); }); } }); gisMapView.finishDraw(); } ``` ### 图形编辑(线/面顶点编辑) ```java // 从点击或查询获得 Feature 后进入编辑模式 gisMapView.startEdit(feature); // 编辑完成后保存或取消 gisMapView.saveCurrentEdit(); // 保存修改并退出 gisMapView.cancelCurrentEdit(); // 取消编辑、丢弃修改 gisMapView.undoEdit() //撤销上一次顶点操作(移动、添加、删除) gisMapView.redoEdit() //重做上一次撤销的顶点操作 ``` ***编辑交互说明:*** ***移动顶点:*** 触摸并拖动顶点的黄色圆点。 ***删除顶点:*** 单击顶点(短按)后确认对话框。 ***添加顶点:*** 在线段上单击即可插入新顶点。 ### 几何变化与顶点选择监听器 **1. 概述** SDK 提供了两组监听器,用于实时监听绘制/编辑过程中几何对象的变化以及编辑模式下顶点的选中状态变化。监听器返回的几何坐标均为 WGS84(EPSG:4326)经纬度。 **注册与移除方法** |方法 |描述| |----|----| addGeometryChangedListener(GeometryChangedListener listener)| 注册一个几何变化监听器。会同时注册到绘制工具和编辑工具。 removeGeometryChangedListener(GeometryChangedListener listener)| 移除指定的几何变化监听器。 addSelectedVertexChangedListener(SelectedVertexChangedListener listener)| 注册一个顶点选择变化监听器(仅作用于编辑工具)。 removeSelectedVertexChangedListener(SelectedVertexChangedListener listener) |移除指定的顶点选择变化监听器。 >注意:若在监听器中执行耗时操作,建议在子线程中处理,避免阻塞 UI 线程。 **使用示例** ```java // 1. 创建监听器实例 GeometryChangedListener geomListener = new GeometryChangedListener() { @Override public void onGeometryChanged(DPPGeometry geometry, boolean isFinished) { // 获取几何类型 String type = geometry.getGeometryType(); // "POINT", "LINE", "POLYGON" // 若需要投影坐标,可在此转换(例如转为 CGCS2000) // Geometry projGeom = CoordinateConverter.transformGeometry(geometry, "EPSG:4326", "EPSG:4490"); if (isFinished) { // 绘制/编辑完成,可保存或显示结果 showToast("几何已确定"); } else { // 实时更新UI,如显示顶点数 int vertexCount = 0; if (geometry instanceof DPPPolyline) { vertexCount = ((DPPPolyline) geometry).getPoints().size(); } else if (geometry instanceof DPPPolygon) { vertexCount = ((DPPPolygon) geometry).getOuterRing().size(); } textView.setText("当前顶点数: " + vertexCount); } } }; SelectedVertexChangedListener vertexListener = new SelectedVertexChangedListener() { @Override public void onSelectedVertexChanged(int selectedIndex, DPPPoint vertex, DPPGeometry geometry) { if (selectedIndex >= 0 && vertex != null) { String coord = String.format("选中顶点 #%d: (%.6f, %.6f)", selectedIndex, vertex.getX(), vertex.getY()); textView.setText(coord); } else { textView.setText("未选中顶点"); } } }; // 2. 注册监听器 mapView.addGeometryChangedListener(geomListener); mapView.addSelectedVertexChangedListener(vertexListener); // 3. 开始绘制或编辑 mapView.startDrawLineByPoint(); // 此时会触发几何变化回调 // 或 mapView.startEdit(feature); // 编辑时触发几何变化和顶点选中回调 // 4. 在 Activity 销毁时及时移除监听,避免内存泄漏 @Override protected void onDestroy() { super.onDestroy(); mapView.removeGeometryChangedListener(geomListener); mapView.removeSelectedVertexChangedListener(vertexListener); } ``` **注意事项** - **坐标系:** 回调中的 DPPGeometry 和 DPPPoint 均为 WGS84 经纬度。若需要投影坐标,可使用 CoordinateConverter.transformGeometry() 或 transformPoint() 进行转换。 - **性能:** 几何变化回调可能在用户拖拽顶点时高频触发,请勿在其中执行重量级操作。 - **工具状态:** 几何变化监听器同时作用于绘制和编辑工具,而顶点选择监听器仅在编辑模式(EditTool)下有效。 - **移除监听:** 务必在组件销毁时移除监听器,否则可能造成内存泄漏。 ### 几何图形高亮 **一、要素高亮管理** |方法签名| 说明| |--------|-----| |`highlightFeatures(String tableName, List featureIds)` |高亮同一张表中的多个要素(先清除所有现有高亮)。 `addHighlightFeature(String tableName, long featureId)` |新增一个高亮要素(保留已有高亮,叠加显示)。 `removeHighlightFeature(String tableName, long featureId)` |移除指定要素的高亮(不影响其他高亮)。 `highlightFeature(DPPFeature feature)` |高亮单个要素(根据 feature 对象的表名和 ID)。 `highlightFeatures(List features)` |高亮多个要素(逐个调用 highlightFeature,保留各自表名)。 `highlightFeature(String tableName, long featureId)` |高亮指定表名的单个要素(底层实现,供上述方法复用)。 `highlightTemporaryFeature(DPPFeature feature)` |高亮临时几何。 `highlightTemporaryFeature(List features)` |高亮临时几何批量。 `addTemporaryHighlight(DPPFeature feature)` |新增一个高亮要素(保留已有高亮,叠加显示)。 `clearTemporaryHighlights()` |移除临时几何高亮。 `clearHighlight()` |清除所有要素的高亮。 **二、设置高亮样式** **方法签名**: `void setHighlightStyle(int fillColor, float fillOpacity, int strokeColor, float strokeWidth, int pointColor, float pointRadius)` **参数说明**: - **fillColor**:面填充色(ARGB) - **fillOpacity**:面透明度(0 ~ 1) - **strokeColor**:线/边框颜色 - **strokeWidth**:线宽(像素) - **pointColor**:点/圆点颜色 - **pointRadius**:点半径(像素) **示例**: ```java /** * 自定义高亮样式。 *

分别设置点、线、面的视觉效果,支持透明度与粗细调节。

* * @param fillColor 面要素填充色 (ARGB 颜色值) * @param fillOpacity 面要素填充不透明度,范围 0.0 (全透明) 到 1.0 (不透明) * @param strokeColor 线要素或面边框的颜色 (ARGB 颜色值) * @param strokeWidth 线要素或面边框的宽度,单位:像素 * @param pointColor 点要素或圆点的颜色 (ARGB 颜色值) * @param pointRadius 点要素或圆点的半径,单位:像素 */ public void setHighlightStyle(int fillColor, float fillOpacity, int strokeColor, float strokeWidth, int pointColor, float pointRadius) { // ... } ``` >默认样式:半透明青色填充、深蓝色边框、红色点。 **三、临时图形显示(GraphicsOverlay)** |方法签名| 说明| |--------|-----| GraphicsOverlay getGraphicsOverlay(String id) |获取或创建指定 ID 的图形覆盖层(可独立管理多个图层)。 showTemporaryGeometry(Geometry geometry, Map attrs) |临时显示一个几何对象(点/线/面/多线/多面),自动处理坐标转换,显示在默认覆盖层上,清除旧图形。 clearTemporaryGeometry() |清除所有临时图形(即默认覆盖层上的图形)。 **GraphicsOverlay 支持的方法**(通过 getGraphicsOverlay() 获得): - clear() – 清除该覆盖层所有图形 - addGraphic(Graphic graphic) – 添加图形 - removeGraphic(Graphic graphic) – 移除指定图形 ***支持的样式属性***(传入 Map): |几何类型 |支持的属性键 |--------|-----| 点(圆点)| color(颜色)、radius(半径像素) 点(图标)| iconId(资源 ID)、iconSize(缩放倍数) 线 |color、width(线宽像素) 面 |fillColor、fillOpacity、strokeColor、strokeWidth、dashArray(虚线数组) 多线 / 多面| 自动遍历所有子要素,样式继承以上规则 ### 地图事件监听 |方法签名| 说明| |--------|-----| setOnMapClickListener(OnMapClickListener listener) |设置地图单击监听器,回调返回点击位置的 DPPLatLng 和该位置下命中的要素列表(List)。 setOnMapLongClickListener(OnMapLongClickListener listener) |设置地图长按监听器,回调参数同单击。 **使用示例** ```java // 单击高亮第一个要素 gisMapView.setOnMapClickListener((point, features) -> { if (!features.isEmpty()) { //小心判断,这里会返可能会有临时、高亮几何,使用哪一类需要取出里面的值判断 gisMapView.highlightFeature(features.get(0)); } }); // 长按清除高亮 gisMapView.setOnMapLongClickListener((point, features) -> { gisMapView.clearHighlight(); }); // 自定义高亮样式 gisMapView.setHighlightStyle(Color.MAGENTA, 0.5f, Color.CYAN, 6f, Color.YELLOW, 14f); // 显示临时缓冲区(示例) DPPPolygon buffer = ...; Map style = new HashMap<>(); style.put("fillColor", "#FFFF00"); style.put("fillOpacity", 0.4f); gisMapView.showTemporaryGeometry(buffer, style); ``` > **setOnMapClickListener返回的features列表中可能会有临时、高亮几何,使用哪一类需要取出里面的值判断** ### 图形样式设置 **静态样式(普通样式)** ***点图层样式*** ```java // 设置点的颜色和半径(单位:像素) gisMapView.setPointStyle(Color.RED, 12f); ``` ***线图层样式*** ```java // 设置线的颜色和宽度(单位:像素) gisMapView.setLineStyle(Color.BLUE, 3f); ``` ***面图层样式*** ```java // 设置面的填充色、边框色、边框宽度、虚线数组 Float[] dashArray = new Float[]{5f, 3f}; // 实线长度5,间隔3 gisMapView.setPolygonStyle(Color.parseColor("#00FF00"), Color.BLACK, 2f, dashArray); ``` ***说明:*** - 静态样式是对整个图层生效的默认样式。 - 如果后续调用了动态样式(如 setDynamicPointStyle),动态样式会覆盖静态样式,直到调用 clearDynamicStyle(tableName) 恢复静态样式。 - 虚线数组可选,若不传则绘制实线。 **恢复默认样式** ```java // 恢复该表的样式为 SDK 默认值(点红色、线红色、面绿色半透明等) gisMapView.getFeatureTable("points").resetStyle(); ``` **动态样式(按属性值渲染)** 根据要素的字段值动态改变点/线/面的颜色。 ***点样式:*** ```java Map pointColors = new HashMap<>(); pointColors.put(1, Color.RED); pointColors.put(2, Color.GREEN); pointColors.put(3, Color.BLUE); gisMapView.setDynamicPointStyle("points", "upload", pointColors, Color.GRAY, 12f); // 也可省略半径参数,使用当前默认半径 gisMapView.setDynamicPointStyle("points", "upload", pointColors, Color.GRAY); ``` ***线样式:*** ```java Map lineColors = new HashMap<>(); lineColors.put("highway", Color.RED); lineColors.put("local", Color.BLUE); gisMapView.setDynamicLineStyle("roads", "type", lineColors, Color.BLACK, 3f); ``` ***面样式:*** ```java Map fillColors = new HashMap<>(); fillColors.put(1, Color.RED); fillColors.put(2, Color.GREEN); gisMapView.setDynamicPolygonStyle("polygons", "upload", fillColors, Color.LTGRAY, Color.BLACK, 2f, new Float[]{2f, 2f}, 0.6f); // 也可单独指定边框映射 Map strokeColors = new HashMap<>(); strokeColors.put(1, Color.WHITE); strokeColors.put(2, Color.YELLOW); gisMapView.setDynamicPolygonStyle("polygons", "upload", fillColors, Color.LTGRAY, strokeColors, Color.BLACK, 2f, new Float[]{2f, 2f}, 0.6f); ``` 清除动态样式,恢复静态样式: ```java gisMapView.clearDynamicStyle("points"); ``` ### 文本标注 **功能说明** 为矢量图层(点、线、面)添加文本标注,支持自定义字段、颜色、大小、偏移、描边等样式。线要素可选“中心点标注”模式,确保任何缩放级别均可见。 **快速上手** ```java // 1. 为 points 表设置标注(显示在点位置) LabelStyle pointStyle = new LabelStyle.Builder() .setFieldName("name") // 使用属性中的 "name" 字段 .setTextColor(Color.RED) // 文字颜色 .setTextSize(16f) // 文字大小(像素) .setAllowOverlap(true) // 允许重叠(避免因碰撞隐藏) .build(); gisMapView.setLabelStyle("points", pointStyle); // 2. 为 lines 表设置标注(推荐使用中心点标注,任何缩放都显示) LabelStyle lineStyle = new LabelStyle.Builder() .setFieldName("name") .setTextColor(Color.YELLOW) .setTextSize(16f) .setCenterOnLine(true) // 关键:线要素中心点显示 .setAllowOverlap(true) .build(); gisMapView.setLabelStyle("lines", lineStyle); // 3. 为 polygons 表设置标注(显示在多边形中心) LabelStyle polygonStyle = new LabelStyle.Builder() .setFieldName("name") .setTextColor(Color.GREEN) .setTextSize(16f) .build(); // 默认 placement = POINT gisMapView.setLabelStyle("polygons", polygonStyle); // 4. 关闭标注 gisMapView.setLabelStyle("points", null); ``` **参数说明** |方法 |说明 |默认值| |------|-----|-----| |`setFieldName(String)` |标注字段名(需在要素属性中存在)| "name" |`setTextColor(int)`| 文字颜色(Color 常量或 ARGB 值) |Color.BLACK |`setTextSize(float)` |文字大小(像素) |14f |`setOffset(float x, float y)`| 文字偏移量(屏幕像素) |(0,0) |`setHaloColor(int)`| 文字描边颜色 |Color.WHITE |`setHaloWidth(float)` |描边宽度 |0f |`setAllowOverlap(boolean)` |是否允许与其他标注重叠 |false |`setPlacement(LabelPlacement)` |放置方式:POINT、LINE、LINE_CENTER |POINT |`setCenterOnLine(boolean)` |线要素专用:强制显示在线的几何中心(任何缩放可见) |false > 提示:对于线要素,强烈推荐设置 setCenterOnLine(true),这样无论缩放级别高低,标注都会稳定显示在线条的中心位置。 **效果示意** - 点图层:标注显示在每个点的位置。 - 线图层(centerOnLine=true):标注显示在线段的中点,始终保持水平,不随缩放隐藏。 - 线图层(普通 LINE_CENTER):标注沿线方向放置,低缩放级别可能不显示。 - 面图层:标注显示在面的几何中心(质心)。 **常见问题** ***Q:标注不显示怎么办?*** A:检查字段名是否正确,字段值是否非空;确认已调用 setLabelStyle 且地图样式已加载;尝试设置 setAllowOverlap(true)。 ***Q:中英文混合字符串显示为乱码或方框?*** A:SDK 已内置离线中文字体,无需额外配置。如果出现乱码,请确认您的设备或模拟器支持 Noto 字体,或联系 SDK 维护者。 ***Q:线标注只在起点显示?*** A:请使用 setCenterOnLine(true) 模式,它会自动将标注放在线的中心点,而不是起点。 ***Q:如何关闭某个图层的标注?*** A:调用 gisMapView.setLabelStyle(tableName, null)。 ### 图层过滤(根据属性动态显示/隐藏要素) ***简单等值过滤:*** ```java gisMapView.setLayerFilter("points", "upload", 2); ``` ***比较运算符:*** ```java gisMapView.setLayerFilter("points", "upload", ">", 5); // >, >=, <, <= ``` ***复杂组合条件:*** ```java Filter complex = Filter.where("upload").gt(5) .and(Filter.where("name").eq("张三")); gisMapView.setLayerFilter("points", complex); ``` ***IN / NOT 条件:*** ```java Filter inFilter = Filter.where("upload").in(1, 2, 3); gisMapView.setLayerFilter("points", inFilter); Filter notFilter = Filter.where("name").eq("李四").not(); gisMapView.setLayerFilter("points", notFilter); ``` ***清除过滤:*** ```java gisMapView.clearLayerFilter("points"); ``` ### 属性查询 除了通过过滤器动态筛选图层外,还可以直接查询数据库返回要素列表。 ```java // 等值查询 gisMapView.queryFeatureByField("points", "upload", 2, new FeatureTable.QueryCallback() { @Override public void onSuccess(List features) { /* 处理结果 */ } @Override public void onError(String error) { } }); // 多条件 AND 查询 Map cond = new HashMap<>(); cond.put("upload", 1); cond.put("name", "某点"); gisMapView.queryFeaturesByFields("points", cond, callback); // 自定义 WHERE 子句 gisMapView.queryFeaturesByWhere("points", "upload > 2 AND name LIKE '%小区%'", callback); ``` ### 图层显隐控制 ```java FeatureTable points = gisMapView.getFeatureTable("points"); points.setVisible(false); // 隐藏点图层(标注同时隐藏) points.setVisible(true); // 显示 ``` ### 地图缩放 SDK 提供了以下地图视口控制方法: ***方法列表*** |方法| 说明| |----|----| |`setCenter(latitude, longitude)` |设置中心点,缩放不变 |`setZoom(zoom)` |设置缩放级别,中心点不变 |`setCenterAndZoom(latitude, longitude, zoom)` |同时设置中心点和缩放级别(无动画) |`animateTo(latitude, longitude, zoom, durationMs)` |带动画移动地图到指定位置并缩放 |`getCenter()` |获取当前中心点 |`getZoom()` |获取当前缩放级别 ***使用示例*** ```java // 定位到甲秀楼,缩放级别 14 gisMapView.setCenterAndZoom(26.5775, 106.7134, 14f); // 只移动中心点(保持原有缩放) gisMapView.setCenter(26.6010, 106.6950); // 黔灵山公园 // 只改变缩放级别(保持中心) gisMapView.setZoom(15f); // 带动画平滑移动到观山湖区 gisMapView.animateTo(26.6395, 106.6215, 13f, 1000); // 获取当前中心点 LatLng center = gisMapView.getCenter(); float zoom = gisMapView.getZoom(); ``` ### 图片标注 ```java // 添加标注(返回唯一 ID) String markerId = gisMapView.addImageMarker(longitude, latitude, R.drawable.ic_location); // 可指定图标大小(1.0 为原始尺寸) gisMapView.addImageMarker(longitude, latitude, R.drawable.ic_pin, 1.5f); // 更新位置 gisMapView.updateImageMarker(markerId, newLongitude, newLatitude); // 移除标注 gisMapView.removeImageMarker(markerId); ``` **说明:** - 图片标注独立于矢量图层,支持半透明、任意尺寸。 - 图标默认允许重叠并忽略放置冲突,确保始终显示。 - 每次添加会自动生成唯一 ID,便于管理多个标注。 - 支持任意 Drawable 资源(包括矢量图)。 - 图标缩放比例建议 0.5~2.0。 - 图标会默认显示在地图最上层,不会被遮挡。 ### 临时图形层(GraphicsOverlay) GraphicsOverlay 是一个轻量级图层管理工具,用于在地图上动态添加 纯色圆点、图片图标 或 线 等临时图形。所有图形均不会持久化到数据库,适合标注重要位置、临时绘制高亮等需求。 ***9.1 获取 GraphicsOverlay 实例*** 在 GisMapView2D 样式加载完成后(建议在 onMapReady 回调中),通过 getGraphicsOverlay(String id) 获取或创建一个覆盖层。 ```java gisMapView.setCallback(new GisMapCallback() { @Override public void onMapReady() { // 确保地图样式已加载 GraphicsOverlay overlay = gisMapView.getGraphicsOverlay("temp"); // 添加图片图标(需要先将图片注册到样式) Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ttss); gisMapView.addImageToStyle("my_icon", bitmap); DPPPoint point = new DPPPoint(106.7134, 26.5775); Graphic iconGraphic = new Graphic(point); iconGraphic.setAttribute("iconId", "my_icon"); iconGraphic.setAttribute("iconSize", 1.5f); overlay.addGraphic(iconGraphic); // 添加红色圆点 Graphic pointGraphic = new Graphic(point); pointGraphic.setAttribute("color", "#FF0000"); pointGraphic.setAttribute("radius", 12); overlay.addGraphic(pointGraphic); // 添加线 DPPPolyline line = new DPPPolyline(Arrays.asList( new DPPPoint(106.7134, 26.5775), new DPPPoint(106.7200, 26.5800) )); Graphic lineGraphic = new Graphic(line); lineGraphic.setAttribute("color", "#0000FF"); lineGraphic.setAttribute("width", 5); overlay.addGraphic(lineGraphic); } }); ``` > 注意:每个 id 对应一个独立的覆盖层,可同时创建多个(例如 “highlight”、“measure” 等)。 ***9.2 添加图形*** 9.2.1 添加纯色圆点 ```java // 创建点坐标(经度, 纬度) DPPPoint point = new DPPPoint(106.7134, 26.5775); Graphic graphic = new Graphic(point); graphic.setAttribute("color", "#FF0000"); // 红色,支持 "#RRGGBB" 格式 graphic.setAttribute("radius", 12); // 半径(像素) overlay.addGraphic(graphic); ``` 9.2.2 添加图片图标 需要先通过 addImageToStyle 将 Drawable 注册到地图样式。 ```java // 注册图片资源 Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_location); gisMapView.addImageToStyle("my_icon", bitmap); // 添加图标标注 Graphic iconGraphic = new Graphic(point); iconGraphic.setAttribute("iconId", "my_icon"); iconGraphic.setAttribute("iconSize", 1.5f); // 图标缩放比例 overlay.addGraphic(iconGraphic); ``` 9.2.3 添加线 ```java DPPPolyline line = new DPPPolyline(Arrays.asList( new DPPPoint(106.7134, 26.5775), new DPPPoint(106.7200, 26.5800) )); Graphic lineGraphic = new Graphic(line); lineGraphic.setAttribute("color", "#0000FF"); // 蓝色 lineGraphic.setAttribute("width", 5); // 线宽(像素) overlay.addGraphic(lineGraphic); ``` 9.3 管理覆盖层 ```jjva // 清除所有图形 overlay.clear(); // 隐藏/显示整个覆盖层(不删除图形) overlay.setVisible(false); overlay.setVisible(true); ``` 9.4 完整示例 ```java gisMapView.setCallback(new GisMapCallback() { @Override public void onMapReady() { // 1. 注册图标 Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_location); gisMapView.addImageToStyle("my_icon", bitmap); // 2. 创建覆盖层 GraphicsOverlay overlay = gisMapView.getGraphicsOverlay("demo"); // 3. 添加红色圆点 DPPPoint point = new DPPPoint(106.7134, 26.5775); Graphic dot = new Graphic(point); dot.setAttribute("color", "#FF0000"); dot.setAttribute("radius", 12); overlay.addGraphic(dot); // 4. 添加图标标记(同一位置) Graphic icon = new Graphic(point); icon.setAttribute("iconId", "my_icon"); icon.setAttribute("iconSize", 1.2f); overlay.addGraphic(icon); // 5. 添加一条线 DPPPolyline line = new DPPPolyline(Arrays.asList( new DPPPoint(106.7134, 26.5775), new DPPPoint(106.7200, 26.5800) )); Graphic lineGraphic = new Graphic(line); lineGraphic.setAttribute("color", "#00FF00"); lineGraphic.setAttribute("width", 4); overlay.addGraphic(lineGraphic); // 6. 移动地图到该区域 gisMapView.animateTo(26.5775, 106.7134, 16f, 500); } }); ``` > 提示:GraphicsOverlay 中的每个图形都会生成独立的地图层,建议单次覆盖层内的图形数量控制在 100 个以内,以免影响性能。如需绘制大量动态点,请优先使用 FeatureTable 的数据库图层。 ### 地图定位与缩放 **定位到当前位置(GPS)** 将地图移动到当前设备位置,并缩放到合适级别: ```java // 1. 获取当前定位(需宿主自行实现,例如使用 LocationManager 或 FusedLocationProviderClient) Location location = ...; // 获取到的位置对象 double lat = location.getLatitude(); double lng = location.getLongitude(); // 2. 直接跳转(无动画) gisMapView.setCenterAndZoom(lat, lng, 15f); // 或者 带动画缓慢移动(慢速缩放) gisMapView.animateTo(lat, lng, 15f, 1000); // 1000毫秒动画 ``` **定位到当前几何图形(在主线程中执行)** ```java // 1. 缩放到几何范围,边距 80 像素,默认动画 gisMapView.zoomToGeometry(geometry, 80); // 2. 缩放到要素,边距 60,动画 1000ms gisMapView.zoomToGeometry(feature.geometry(), 60, 1000); // 3. 简写:直接传要素 gisMapView.zoomToFeature(feature, 80); ``` ### 定位组件 **一、权限要求** 在调用任何定位方法前,必须申请以下权限之一: ```xml ``` 议使用 ACCESS_FINE_LOCATION 以获得最高精度。SDK 不会自动请求权限,请宿主 App 在适当位置(如 onCreate)自行请求并处理用户授权结果。 **二、启用定位** 调用 enableLocation 方法,传入四个参数: ```java gisMapView.enableLocation( LocationRenderMode.COMPASS, // 定位图标样式 CameraTrackingMode.TRACKING_COMPASS, // 相机追踪模式 true, // 是否显示脉冲动画 0.2f // 精度圈透明度 (0~1) ); ``` **参数详解** | 参数| 类型| 说明| |--------|------|------| |renderMode| LocationRenderMode |定位图标的视觉样式(见下表) |cameraMode| CameraTrackingMode| 地图相机跟随模式(见下表) |enablePulse| boolean| true 显示脉冲扩散动画;false 不显示 |accuracyAlpha| float| 精度圈透明度,范围 0.0(完全透明)~ 1.0(完全不透明),建议 0.2 ***LocationRenderMode 枚举*** |枚举值 |效果 |适用场景| |--------|------|------| |NORMAL| 普通蓝点(无方向指示)| 仅显示位置,不关心朝向 |COMPASS| 方向箭头(指示设备朝向)| 步行导航、地图随方向旋转 |GPS |导航大图标(带朝向小三角)| 驾驶导航,图标更大更明显 ***CameraTrackingMode 枚举*** |枚举值 |效果| |-------|------| |NONE |地图自由,相机不跟随位置移动 |TRACKING |相机跟随位置移动,但地图方向保持不变(始终保持正北向上) |TRACKING_COMPASS| 相机跟随位置移动,同时自动旋转地图使设备朝向始终指向屏幕上方(类似导航模式) **三、接收实时位置更新** 通过 setOnLocationChangeListener 注册回调,即可持续获得最新的设备位置(频率由系统决定,约 1 次/秒)。 ```java gisMapView.setOnLocationChangeListener(location -> { // 回调在主线程,可直接更新 UI double lat = location.getLatitude(); double lng = location.getLongitude(); TextView tvCoord = findViewById(R.id.tv_coord); tvCoord.setText(String.format("纬度: %.6f, 经度: %.6f", lat, lng)); }); ``` |***注意:*** 该回调与地图上显示的定位图标位置完全相同(已通过 forceLocationUpdate 强制同步),无数据不一致问题。 **四、运行时切换模式** 定位启用后,可以动态改变图标样式和相机追踪模式。 ```java // 切换为普通蓝点 gisMapView.setLocationRenderMode(LocationRenderMode.NORMAL); // 切换为仅跟随位置(地图不自动旋转) gisMapView.setLocationCameraMode(CameraTrackingMode.TRACKING); ``` **五、获取最后已知位置(一次性)** 若只需当前缓存的位置(例如初始化地图中心到用户位置),使用同步方法: ```java Location lastLoc = gisMapView.getLastKnownLocation(); if (lastLoc != null) { gisMapView.animateTo(lastLoc.getLatitude(), lastLoc.getLongitude(), 16f, 500); } ``` |***注意:*** 该方法返回的是系统最后缓存的位置,可能为 null 或时效性较差,建议在获得实时回调后使用回调中的位置。 **六、禁用定位** 当不再需要定位时(例如 Activity 的 onPause 或 onDestroy),调用 disableLocation 释放资源、停止位置更新。 ```java gisMapView.disableLocation(); ``` **七、完整示例(含权限请求)** ```java public class MainActivity extends AppCompatActivity { private GisMapView2D gisMapView; private static final int LOCATION_PERMISSION_CODE = 100; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); gisMapView = findViewById(R.id.gisMapView); // 初始化地图(配置底图等,略) gisMapView.setCallback(() -> { // 地图就绪后请求权限 requestLocationPermission(); }); } private void requestLocationPermission() { if (checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) { requestPermissions(new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, LOCATION_PERMISSION_CODE); } else { startLocation(); } } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { if (requestCode == LOCATION_PERMISSION_CODE && grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { startLocation(); } else { Toast.makeText(this, "需要定位权限以显示当前位置", Toast.LENGTH_SHORT).show(); } } private void startLocation() { // 启用定位并开始接收位置 gisMapView.enableLocation( LocationRenderMode.COMPASS, CameraTrackingMode.TRACKING_COMPASS, true, 0.2f ); // 实时监听位置 gisMapView.setOnLocationChangeListener(location -> { runOnUiThread(() -> { // 例如更新坐标显示 updateLocationUI(location); }); }); } @Override protected void onPause() { super.onPause(); // 可选:暂停定位以节省电量 gisMapView.disableLocation(); } @Override protected void onResume() { super.onResume(); // 若之前禁用了,可重新启用(注意权限) if (checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) { startLocation(); } } private void updateLocationUI(Location location) { // 更新 TextView、记录轨迹等 } } ``` ### 外部高精度定位注入 如果您使用 RTK、蓝牙 GNSS 接收器等外部设备获得了更高精度的坐标,可以调用以下接口在地图上显示定位图标和精度圈。 **初始化定位显示** ```java gisMapView.initLocationDisplay(LocationRenderMode.COMPASS, true, 0.2f); ``` **更新位置** ```java // 纬度、经度、精度(米)、方向角(度) gisMapView.updateExternalLocation(26.5775, 106.7134, 0.5f, 45.0f); ``` | 精度圈半径会自动根据 accuracy 参数绘制;方向角仅在 COMPASS 或 GPS 模式下生效。 **隐藏定位图标** ```java gisMapView.hideLocationDisplay(); ``` **与内置定位的对比** |特性 |内置 enableLocation |外部注入 initLocationDisplay| |----|----|----| 数据源| SDK 内部 LocationManager |您的前台服务 / RTK 设备 后台运行| ❌ 受限(会被系统限频) |✅ 由您控制(前台服务保活) 脉冲动画 / 精度圈| ✅ 自动显示| ✅ 依然可用(通过注入保持) 追踪模式| ✅ 自动跟随 |✅ 依然可用 电量控制| SDK 内部管理 |由您完全控制(可灵活开关定位) ### 自定义定位图标(独立图层) 如果您不需要完整的定位组件(如脉冲动画、精度圈、追踪模式),只想在地图上显示一个图标表示当前位置,可使用此方法。 ```java // 1. 添加定位图标(通常在地图准备好后调用一次) String locationSourceId = gisMapView.addLocationIcon(R.drawable.ic_location, 1.5f); // 2. 当获取到新位置时,更新图标位置 gisMapView.updateLocationIcon(locationSourceId, latitude, longitude); // 3. 移除定位图标(释放资源,可选) gisMapView.removeLocationIcon(locationSourceId); ``` **说明:** - addLocationIcon 会自动注册图片并创建独立图层,返回的 sourceId 用于后续更新。 - updateLocationIcon 只更新数据源,性能高效。 - removeLocationIcon 会移除图层、数据源和图片资源,建议在 Activity 销毁时调用。 - 请确保在地图样式加载完成后(onMapReady 回调中)调用这些方法。 ### 地图旋转跟随设备方向 ```java // 仅开启地图旋转跟随,不显示定位图标 gisMapView.setMapRotationFollowingEnabled(true); // 关闭跟随 gisMapView.setMapRotationFollowingEnabled(false); ``` **设置地图方位角** 您可以通过编程方式旋转地图方向(0° = 正北,顺时针增加),支持无动画直接跳转或平滑动画。 | 方法 | 说明 | |------|------| | `setBearing(double bearing)` | 立即将地图旋转到指定角度(无动画) | | `animateBearing(double bearing, long durationMs)` | 带动画旋转到指定角度,时长毫秒 | ```java // 无动画旋转到 90°(正东) gisMapView.setBearing(90.0); // 带动画旋转到 180°(正南),动画时长 500ms gisMapView.animateBearing(180.0, 500); ``` ***传感器实时旋转地图(电子罗盘)*** 您可以利用设备的方向传感器,使地图自动跟随设备旋转,实现“电子罗盘”效果。 ***示例:使用旋转向量传感器*** ```java public class MainActivity extends AppCompatActivity { private GisMapView2D gisMapView; private SensorManager sensorManager; private Sensor rotationVectorSensor; private SensorEventListener compassListener; private void startCompass() { sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE); rotationVectorSensor = sensorManager.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR); if (rotationVectorSensor == null) { Toast.makeText(this, "设备不支持方向传感器", Toast.LENGTH_SHORT).show(); return; } compassListener = new SensorEventListener() { @Override public void onSensorChanged(SensorEvent event) { if (event.sensor.getType() == Sensor.TYPE_ROTATION_VECTOR) { float[] rotationMatrix = new float[16]; SensorManager.getRotationMatrixFromVector(rotationMatrix, event.values); float[] orientation = new float[3]; SensorManager.getOrientation(rotationMatrix, orientation); float bearing = (float) Math.toDegrees(orientation[0]); bearing = (bearing + 360) % 360; // 实时旋转地图(无动画,确保跟手) gisMapView.setBearing(bearing); } } @Override public void onAccuracyChanged(Sensor sensor, int accuracy) {} }; sensorManager.registerListener(compassListener, rotationVectorSensor, SensorManager.SENSOR_DELAY_UI); } @Override protected void onResume() { super.onResume(); if (sensorManager != null && compassListener != null && rotationVectorSensor != null) { sensorManager.registerListener(compassListener, rotationVectorSensor, SensorManager.SENSOR_DELAY_UI); } } @Override protected void onPause() { super.onPause(); if (sensorManager != null && compassListener != null) { sensorManager.unregisterListener(compassListener); } } } ``` > 注意:传感器需要在 AndroidManifest.xml 中无需额外权限,但部分设备可能需要 android.permission.INTERNET(实际不影响)。请确保在 onResume 注册、onPause 注销,以节省电量。 **与内置指南针对比** |特性 |内置指南针 |自定义指南针| |----|----|----| 图标自定义 |❌ 仅替换系统图标 |✅ 任意 drawable 尺寸调整 |❌ 固定 |✅ 任意 dp 值 位置调整 |仅预设 Gravity,有限边距 |任意 Gravity + 四周边距 点击行为 |固定:复位正北 |可自定义(默认:切换电子罗盘跟随) 淡出动画 |朝向正北时自动淡出 |始终显示 ### 指北针 **地图回正指北针** 地图默认会在右上角显示一个指北针(仅在用户旋转地图后短暂出现)。您可以通过 GisMapOptions 配置其行为。 >指南针配置显示后点击后自身方向重置为默认方向,无法监听其行为。 **一直显示 & 自定义图标** ```java GisMapOptions options = new GisMapOptions.Builder() // ... 其他配置 .setCompassEnabled(true) // 启用指北针 .setCompassFadeWhenFacingNorth(false) // 关键:一直显示(不淡出) .setCompassGravity(Gravity.TOP | Gravity.END)// 位置(右上角) .setCompassMargins(16, 16, 16, 16) // 边距(像素) .setCompassImage(R.drawable.my_compass) // 自定义图标(可选) .build(); gisMapView.setOptions(options); ``` ***运行时控制*** ```java // 动态开关指北针 gisMapView.setCompassEnabled(false); // 改变位置和边距 gisMapView.setCompassGravity(Gravity.LEFT | Gravity.BOTTOM); gisMapView.setCompassMargins(10, 10, 10, 10); // 更换图标 gisMapView.setCompassImage(R.drawable.new_compass); ``` > 指北针内置交互:点击后地图会自动平滑旋转回正北(bearing = 0),无需额外编码。 **开启/关闭电子罗盘跟随(地图随设备旋转)** ***公开 API*** |方法 |说明| |----|----| enableCustomCompass(boolean enabled) |启用/禁用自定义指南针(启用后自动隐藏内置指南针) setCompassIcon(int resId) |设置指南针图标(任意 drawable 资源) setCompassSize(int sizeDp) |设置指南针容器尺寸(单位:dp,图标会在容器内按比例缩放) setCompassPosition(int gravity, int leftDp, int topDp, int rightDp, int bottomDp) |设置指南针位置(重力方向 + 四周边距,单位 dp) ***使用示例*** ```java // 配置并启用 gisMapView.setCompassIcon(R.drawable.ic_compass); gisMapView.setCompassSize(48); gisMapView.setCompassPosition(Gravity.TOP | Gravity.END, 0, 0, 16, 16); gisMapView.enableCustomCompass(true); ``` ***默认交互行为*** 启用自定义指南针后,点击指南针会 ***自动切换*** 设备方向跟随状态: - 首次点击:开启电子罗盘(地图随设备旋转) - 再次点击:关闭电子罗盘,并自动回正至正北(bearing = 0°) >此功能依赖于设备的旋转向量传感器(Sensor.TYPE_ROTATION_VECTOR)。若传感器不可用,点击无效果,但指南针仍能显示地图方位角。 ### Callout 使用说明 Callout 是 SDK 提供的地图要素信息弹窗组件,支持在点击地图时弹出带箭头的气泡,并显示自定义内容。气泡会锚定在地图的指定坐标点,并自动跟随地图移动、缩放。 **一、基本用法** ***1. 获取 Callout 实例*** ```java Callout callout = gisMapView.getCallout(); ``` >***注意:*** Callout 采用懒加载,首次调用 getCallout() 时创建实例,之后复用。 ***2. 显示 Callout*** SDK 提供多种重载方法,可根据场景选用: |方法 |说明| |-----|-----| showCallout(DPPLatLng position) |缩放到点(zoom=16),在指定位置显示 Callout showCallout(DPPGeometry geometry) |缩放到几何范围(边距默认 80px),在几何中心显示 Callout showCallout(DPPGeometry geometry, int padding) |缩放到几何范围,自定义边距,在几何中心显示 Callout showCallout(DPPLatLng position, DPPGeometry geometry, int padding) |缩放到几何范围,自定义边距,在指定 position 显示 Callout showCallout(DPPFeature feature) |缩放到要素几何范围(边距默认 80px),在要素几何中心显示 Callout ```java // 示例:点击要素后显示 Callout gisMapView.setOnMapClickListener(new OnMapClickListener() { @Override public void onClick(DPPLatLng point, List features) { if (!features.isEmpty()) { // 1. 获取要素 DPPFeature feature = features.get(0); // 2. 设置自定义内容(见下文) View contentView = ...; gisMapView.getCallout().setContentView(contentView); // 3. 显示(自动缩放到要素并弹窗) gisMapView.showCallout(feature); } else { gisMapView.dismissCallout(); } } }); ``` ***3. 关闭 Callout*** ```java gisMapView.dismissCallout(); ``` >在点击地图空白处或需要关闭弹窗的场景中调用。 **二、自定义内容视图** Callout 支持完全自定义的内容视图,可以是 XML 布局或代码动态构建。 ***方式一:使用 XML 布局*** ```xml ``` 在代码中: ```java View contentView = LayoutInflater.from(context).inflate(R.layout.callout_custom, null); TextView tvTitle = contentView.findViewById(R.id.tvTitle); TextView tvDesc = contentView.findViewById(R.id.tvDesc); tvTitle.setText("要素名称"); tvDesc.setText("描述信息"); callout.setContentView(contentView); gisMapView.showCallout(feature); ``` ***方式二:代码动态构建*** ```java LinearLayout layout = new LinearLayout(context); layout.setOrientation(LinearLayout.VERTICAL); TextView title = new TextView(context); title.setText("标题"); layout.addView(title); callout.setContentView(layout); ``` >***提示:*** 若内容中使用 ListView 或 RecyclerView,请确保其高度能自适应内容(例如 layout_height="wrap_content"),并配合 setListViewHeightBasedOnChildren 等技巧,避免底部空白 **三、样式自定义** Callout 提供多种样式调整方法,可在显示前调用。 |方法 |说明| 默认值| |----|----|----| setArrowSize(int sizeDp)| 设置箭头大小(单位:dp)| 40 setOffset(int dx, int dy)| 设置弹窗偏移量(像素),正值向右/向下,负值向左/向上 |(0,0) ```java Callout callout = gisMapView.getCallout(); callout.setArrowSize(50); // 箭头更大 callout.setOffset(0, -10); // 整体上移 10px,避免遮挡 ``` **四、交互与事件** ***1. 监听 Callout 关闭事件*** ```java callout.setOnDismissListener(new Callout.OnCalloutDismissListener() { @Override public void onDismiss() { // 弹窗关闭后执行逻辑,例如清除高亮 gisMapView.clearHighlight(); } }); ``` ***2. 在内容视图中添加按钮交互*** ```java Button btnDetail = contentView.findViewById(R.id.btnDetail); btnDetail.setOnClickListener(v -> { // 执行操作(如跳转详情) // 关闭弹窗 gisMapView.dismissCallout(); }); ``` **完整示例(点击要素弹窗)** ```java gisMapView.setOnMapClickListener(new OnMapClickListener() { @Override public void onClick(DPPLatLng point, List features) { gisMapView.clearHighlight(); gisMapView.dismissCallout(); gisMapView.clearTemporaryGeometry(); if (!features.isEmpty()) { Callout callout = gisMapView.getCallout(); // 1. 准备数据列表 List> dataList = new ArrayList<>(); for (DPPFeature feature : features) { String name = (String) feature.getProperty("name"); if (name == null) continue; String desc = (String) feature.getProperty("descriptions"); Map item = new HashMap<>(); item.put("name", name); item.put("desc", desc != null ? desc : "无备注"); dataList.add(item); gisMapView.highlightFeature(feature); } if (dataList.isEmpty()) return; // 2. 创建自定义适配器 ArrayAdapter> adapter = new ArrayAdapter>( MainActivity.this, R.layout.callout_item, dataList ) { @NonNull @Override public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) { if (convertView == null) { convertView = LayoutInflater.from(MainActivity.this) .inflate(R.layout.callout_item, parent, false); } Map item = getItem(position); TextView tvName = convertView.findViewById(R.id.tvItemName); TextView tvDesc = convertView.findViewById(R.id.tvItemDesc); tvName.setText(item == null ? "无名称" : item.get("name")); tvDesc.setText("bianji"); tvDesc.setOnClickListener(v -> { System.out.println("点击了编辑"); }); return convertView; } }; // 3. 加载自定义布局并设置适配器 View contentView = LayoutInflater.from(MainActivity.this).inflate(R.layout.callout_custom, null); ListView listView = contentView.findViewById(R.id.listViewFeatures); listView.setAdapter(adapter); // 4. 设置到 Callout 并显示 callout.setContentView(contentView); gisMapView.showCallout(features.get(0).geometry()); } } @Override public void onError(String error) { System.out.println("ksdljf"); } }); ``` callout_custom.xml ```xml ``` callout_item.xml ```xml ``` ## 高级功能 ### 分割、合并 |操作| 支持情况| 方法| |----|-----|----| |分割线 | ✅ |`gisMapView.splitFeature(line, splitLine, callback)` |分割面 | ✅ |`gisMapView.splitFeature(polygon, splitLine, callback)` |合并线 | ✅ |`gisMapView.mergeFeatures(lineList, callback)` |合并面 | ✅ |`gisMapView.mergeFeatures(polygonList, callback)` ### 加载Geoserver服务 **一、WMS 服务(Web Map Service)** WMS 可用于显示栅格地图(瓦片)或进行点查询(GetFeatureInfo)。 ***1. 添加 WMS 图层(用于显示)*** ```java // 方式一:使用默认参数(SRS=EPSG:3857,PNG 格式) String wmsLayerId = gisMapView.addWmsLayer("http://your.server/wms", "your_layer_name"); // 方式二:指定坐标系和图片格式 String wmsLayerId = gisMapView.addWmsLayer("http://your.server/wms", "your_layer_name", "EPSG:4326", "image/png"); // 方式三:完全自定义参数 gisMapView.addWmsLayer("http://your.server/wms", "your_layer_name", "EPSG:3857", "image/png", true, 256, "custom_wms_id"); ``` ***2. 移除 WMS 图层*** ```java gisMapView.removeWmsLayer(wmsLayerId); ``` ***3. 控制 WMS 图层可见性*** ```java gisMapView.setWmsLayerVisible(wmsLayerId, true); // 显示 gisMapView.setWmsLayerVisible(wmsLayerId, false); // 隐藏 ``` ***4. 点击查询 WMS 要素属性(GetFeatureInfo)*** ```java gisMapView.wmsQuery(point, wmsLayerId, new OnFeatureQueryListener() { @Override public void onSuccess(List features) { // features 包含查询到的要素列表,每个 DPPFeature 包含几何和属性 if (!features.isEmpty()) { DPPFeature feature = features.get(0); // 处理结果,例如高亮显示 gisMapView.highlightFeature(feature); } } @Override public void onError(String error) { Toast.makeText(context, "查询失败: " + error, Toast.LENGTH_SHORT).show(); } }); ``` > 说明:point 为 DPPLatLng 类型,即地图点击坐标。查询结果中的 DPPFeature 包含要素几何(可通过 getGeometry() 获取)和属性字段。 **二、WMTS 服务(Web Map Tile Service)** WMTS 仅用于显示栅格底图(预切瓦片),不支持属性查询。 ***1. 添加 WMTS 图层*** ```java // 方式一:使用默认参数(EPSG:3857,PNG 格式) gisMapView.addWmtsLayer("http://your.server/geoserver/gwc/service/wmts", "your_layer_name", "wmts_source", "wmts_layer"); // 方式二:自定义格式和瓦片矩阵集 gisMapView.addWmtsLayer("http://your.server/geoserver/gwc/service/wmts", "your_layer_name", "wmts_source", "wmts_layer", "image/png", "EPSG:4326"); ``` ***2. 移除 WMTS 图层*** ```java gisMapView.removeWmtsLayer("wmts_source", "wmts_layer"); ``` ***3. 控制 WMTS 图层可见性*** ```java gisMapView.setWmtsLayerVisible("wmts_layer", true); // 显示 gisMapView.setWmtsLayerVisible("wmts_layer", false); // 隐藏 ``` > 注意:WMTS 服务的 URL 必须包含 {x}、{y}、{z} 占位符,SDK 会自动替换。如果遇到 TileOutOfRange 错误,请检查服务的瓦片矩阵范围,或改用 WMS 动态瓦片。 **三、WFS 服务(Web Feature Service)** WFS 用于查询矢量要素(返回 GeoJSON),支持属性过滤和空间查询。 ***1. 点查询 WFS 要素*** ```java gisMapView.wfsQuery(point, "http://your.server/geoserver/wfs", "your_type_name", new OnFeatureQueryListener() { @Override public void onSuccess(List features) { // features 包含查询到的矢量要素列表 for (DPPFeature feature : features) { // 获取几何和属性 DPPGeometry geometry = feature.getGeometry(); Map attrs = feature.getProperties(); } } @Override public void onError(String error) { Toast.makeText(context, "查询失败: " + error, Toast.LENGTH_SHORT).show(); } }); ``` > 说明:WFS 查询默认使用点击点周围 0.01 度的缓冲区(可通过 QueryOptions 调整),返回的 DPPFeature 包含完整几何和属性。 **四、常见问题** |问题现象 |可能原因| 解决方法| |----|----|----| |WMS 图层不显示| 服务地址或图层名错误;坐标系不匹配| 检查 URL 和图层名,确保 SRS 与地图投影一致(通常用 EPSG:3857) |WMTS 显示空白或报 TileOutOfRange| 请求的瓦片超出服务范围| 改用 WMS 动态瓦片,或限制 minZoom/maxZoom |点击查询无结果| BBOX 参数错误;服务不支持 GetFeatureInfo| 确保使用正确的 SRS=EPSG:4326,检查服务端日志 |查询结果为空列表| 点击位置无要素;查询半径太小| 增大缓冲区(QueryOptions.Builder().setBboxBuffer(0.02)) ### 测量工具 **一、概述** 提供两套测量能力: - **交互式测量:** 在地图上点击添加点,自动计算并显示长度(折线)或周长+面积(闭合多边形),结果以 **地图标注** 形式固定在几何中心,随地图平移/缩放同步移动。 - **编程测量:** 直接传入几何对象(点、折线、多边形),计算距离、长度或面积,返回数值供业务使用。 **二、交互式测量** ***2.1 启动测量模式*** ```java // 测量折线长度(默认) gisMapView.startMeasure(); // 或指定类型 gisMapView.startMeasure(MeasureTool.MeasureType.LINE); // 测量长度 gisMapView.startMeasure(MeasureTool.MeasureType.AREA); // 测量面积(自动闭合) gisMapView.undo(); // 撤销 gisMapView.redo(); // 重做 ``` |参数| 说明| |----|----| |MeasureType.LINE |折线测量,实时显示总长度,结果锚定在线段中点。 |MeasureType.AREA |多边形面积测量,当点数 ≥ 3 时自动闭合,实时显示周长和面积,结果锚定在多边形质心。 ***交互行为:*** - ***添加点:*** 在地图上 单击 添加测量点(红圈标记)。 - ***完成测量:*** 长按 地图完成测量,结果保留在地图上。 - ***退出模式:*** 调用 stopMeasure() 或 clearMeasureResult() 清除图形并退出。 >⚠️ 交互式测量会占用触摸事件,测量期间地图平移/缩放依然可用,但点击事件由测量工具消费。 **2.2 停止/清除测量** ```java // 退出测量模式并清除所有图形和文本 gisMapView.stopMeasure(); // 仅清除当前结果(若工具已退出,也清除残留图形) gisMapView.clearMeasureResult(); ``` **2.3 单位设置** 默认单位为 米(m) 和 平方米(m²)。如需更改,请在启动测量模式前设置: ```java // 获取 MeasureTool 实例(需 SDK 提供 getter) MeasureTool measureTool = gisMapView.getMeasureTool(); measureTool.setLengthUnit(UnitConverter.LengthUnit.KILOMETER); measureTool.setAreaUnit(UnitConverter.AreaUnit.HECTARE); // 然后启动测量 gisMapView.startMeasure(MeasureTool.MeasureType.AREA); ``` **2.4 结果展示** - ***线测量:*** 在线的中点显示 "长度: 12.34 m"(颜色黑底白边)。 - ***面测量:*** 在闭合多边形质心显示: ```text 周长: 45.67 m 面积: 123.45 m² ``` 结果固定在地图上,会随地图移动、缩放而同步移动,不会飘出屏幕。 **三、编程测量(直接计算)** 无需交互,直接调用 GisMapView2D 或 MeasureUtils 中的静态方法。 ***3.1 方法列表*** |方法| 说明| |----|----| |measureDistance(p1, p2, unit)| 计算两点间球面距离| |measureLength(polyline, unit) |计算折线总长度| |measureArea(polygon, unit)| 计算多边形面积(自动闭合)| ***3.2 使用示例*** ```java // 1. 距离 DPPPoint p1 = new DPPPoint(106.7134, 26.5775); DPPPoint p2 = new DPPPoint(106.7200, 26.5800); double distKm = gisMapView.measureDistance(p1, p2, UnitConverter.LengthUnit.KILOMETER); // 2. 折线长度 DPPPolyline line = new DPPPolyline(Arrays.asList(p1, p2)); double lenM = gisMapView.measureLength(line, UnitConverter.LengthUnit.METER); // 3. 多边形面积 List pts = Arrays.asList( new DPPPoint(106.7134, 26.5775), new DPPPoint(106.7200, 26.5800), new DPPPoint(106.7150, 26.5850) ); double areaHa = gisMapView.measureArea(pts, UnitConverter.AreaUnit.HECTARE); ``` **四、单位与格式化** ***4.1 长度单位*** |枚举| 中文| 换算| |----|----|----| |METER| 米 |1 m| |KILOMETER |千米 |1 km = 1000 m |MILE| 英里 |1 mi = 1609.344 m |FEET |英尺 |1 ft = 0.3048 m ***4.2 面积单位*** |枚举| 中文| 换算| |----|----|----| |SQUARE_METER |平方米| 1 m² |SQUARE_KILOMETER| 平方公里| 1 km² = 1,000,000 m² |HECTARE |公顷 |1 ha = 10,000 m² |ACRE |英亩 |1 ac ≈ 4046.86 m² ***4.3 格式化辅助*** ```java // 直接获得带单位的字符串 String lengthStr = UnitConverter.formatLength(1234.56, UnitConverter.LengthUnit.KILOMETER); // 输出: "1.23 km" String areaStr = UnitConverter.formatArea(50000, UnitConverter.AreaUnit.HECTARE); // 输出: "5.00 ha" ``` **五、注意事项** - **坐标系:** 所有测量基于 WGS84 经纬度,使用 Haversine 公式(距离)和 球面多边形面积算法,适用于常规 GIS 应用。 - **交互模式:** 测量模式会接管单击/长按事件,但地图的缩放/平移手势仍然可用。 - **结果保留:** 长按完成测量后,图形和文本会继续保留在地图上,直到调用 clearMeasureResult() 或重新开始测量。 - **自动闭合:** 面测量模式下,当点数 ≥ 3 时 自动闭合,无需手动点击首点。 - **精度:** 对于大范围(跨经纬度 > 10°)面积计算,建议使用投影坐标系以提高精度。 ### 内置测量工具 内置测量工具提供了测量卡片,可以直接使用; **xml** ```xml ``` **java** ```java MeasureToolbarView toolbar = findViewById(R.id.measure_toolbar); toolbar.attachToMap(gisMapView2D); toolbar.setOnMeasureListener(new MeasureToolbarView.OnMeasureListener() { @Override public void onMeasureResult(double value, String unit) {/* 处理结果 */ } @Override public void onMeasureClear() {/* 清除 */ } @Override public void onMeasureCancel() {/* 取消 */ } }); toolbar.show();//显示测量工具框 toolbar.hide();//隐藏测量工具框 ``` ### 要素移动(Move Feature) 要素移动功能允许用户通过拖拽方式,将点、线、面要素整体平移到新位置。移动过程中会显示一个临时的半透明图形(样式与原要素完全一致),松手后弹出确认对话框,用户可选择保存或取消。 ***交互流程:*** - 用户在地图上点击某个要素(使其高亮); - 调用 gisMapView.startMoveFeature(feature, listener) 进入移动模式; - 在屏幕上任意位置拖拽,临时图形会跟随手指移动(原始要素位置不变); - 松手后弹出对话框,询问是否保存; - 点击“保存”后,原始要素被移动到新位置,地图刷新,临时图形消失; - 点击“取消”或长按地图,则退出移动模式,位置不变。 ***公开方法:*** |方法 |说明| |----|----| |`startMoveFeature(DPPFeature feature, MoveTool.OnMoveCompleteListener listener)`| 开始移动指定的要素。listener 用于接收移动成功或取消的回调。 |`cancelMove()`| 手动取消当前移动(如外部按钮),直接退出移动模式。 ***使用示例:*** ```java // 假设 dppFeature 是用户通过点击选择的高亮要素 gisMapView.startMoveFeature(dppFeature, new MoveTool.OnMoveCompleteListener() { @Override public void onMoveSuccess(DPPFeature newFeature) { runOnUiThread(() -> { Toast.makeText(MainActivity.this, "移动成功,新ID=" + newFeature.id(), Toast.LENGTH_SHORT).show(); gisMapView.refreshGeoPackage(); gisMapView.clearHighlight(); }); } @Override public void onMoveCancel() { runOnUiThread(() -> Toast.makeText(MainActivity.this, "移动取消", Toast.LENGTH_SHORT).show()); } }); ``` ***注意事项:*** - 移动模式会临时禁用地图的平移、缩放、旋转等手势,避免与拖拽冲突。退出移动模式后自动恢复原手势设置。 - 移动后的要素会保持原有的所有属性字段(如名称、类型等)不变。 - 临时图形的样式完全继承自原始要素所在表的当前样式(包括颜色、线宽、半径等),确保视觉上与原要素一致。 - 移动过程中,原始要素会保持高亮显示(若已高亮),临时图形叠加在上方,方便对比移动前后的位置。 ***相关接口:*** - MoveTool.OnMoveCompleteListener:移动完成监听器,包含 onMoveSuccess(DPPFeature) 和 onMoveCancel() 两个回调。 - 若需要在移动过程中禁用某个外部按钮,可通过 MoveTool.isActive() 判断当前是否正在移动。 > 提示:如果用户不希望通过按钮启动移动,也可以在地图长按监听中直接调用 startMoveFeature,实现“长按要素并拖动”的效果。但为了 SDK 的通用性,我们提供了按钮式启动方式,您可根据实际需求自由封装。 ### 裁剪(Clip)功能 裁剪(Clip)是 GIS 分析中常用的空间操作,使用一个裁剪面去切割一个源要素,只保留源要素位于裁剪面内部的部分,并生成一个新的要素(同时删除原要素)。 ***功能特性*** - 支持裁剪点、线、面三种几何类型。 - 裁剪几何必须是面(Polygon)或多面(MultiPolygon)。 - 异步执行,不阻塞 UI。 - 自动处理坐标系转换(WGS84 ↔ CGCS2000)。 - 裁剪成功后原要素被删除,新要素插入同一表中。 ***交互流程(示例 UI 操作)*** - 选中要裁剪的要素:在地图上点击某个点/线/面,SDK 会将其高亮显示。 - 启动裁剪:点击“启动裁剪”按钮,SDK 记录当前选中的要素,并自动进入多边形绘制模式。 - 绘制裁剪面:在地图上绘制一个面(可使用“点状面”或“自由面”工具)。 - 执行裁剪:点击“执行裁剪”按钮,SDK 用绘制的面裁剪之前选中的要素。 - 查看结果:裁剪成功后,地图刷新,原要素消失,新要素显示在裁剪面内部。 - 取消裁剪:若中途想放弃,可点击“取消裁剪”按钮,清空所有临时状态并退出绘制模式。 | 💡 提示:裁剪操作需要用户手动启动和确认,不会自动保存未确认的绘制。 ***公开 API*** ```java /** * 裁剪要素(用指定面几何裁剪矢量要素) * @param feature 要裁剪的要素(DPPFeature 对象) * @param clipGeometry 裁剪几何(必须是 DPPPolygon 或 DPPMultiPolygon,WGS84坐标系) * @param callback 异步回调,包含 onSuccess(DPPFeature) 和 onError(String) */ public void clipFeature(DPPFeature feature, DPPGeometry clipGeometry, ClipCallback callback) ``` ***使用示例(基于 MainActivity 的简化代码)*** ```java // 假设已通过地图点击获取了 selectedFeature DPPFeature selectedFeature = ...; // 启动裁剪(记录要素,并开始绘制) Button btnClipBegin = findViewById(R.id.btn_clip_begin); btnClipBegin.setOnClickListener(v -> { if (selectedFeature == null) { Toast.makeText(context, "请先点击要素", Toast.LENGTH_SHORT).show(); return; } gisMapView.startDrawPolygonByPoint(); // 启动面绘制 // 自行维护状态标志 }); // 执行裁剪 Button btnClipExec = findViewById(R.id.btn_clip_exec); btnClipExec.setOnClickListener(v -> { DrawResult drawResult = gisMapView.getLastDrawResult(); if (drawResult == null || !(drawResult.getGeometry() instanceof DPPPolygon)) { Toast.makeText(context, "请先绘制有效的裁剪面", Toast.LENGTH_SHORT).show(); return; } DPPPolygon clipPolygon = (DPPPolygon) drawResult.getGeometry(); gisMapView.clipFeature(selectedFeature, clipPolygon, new ClipCallback() { @Override public void onSuccess(DPPFeature clippedFeature) { runOnUiThread(() -> { Toast.makeText(context, "裁剪成功", Toast.LENGTH_SHORT).show(); gisMapView.refreshGeoPackage(); gisMapView.clearHighlight(); // 重置状态 }); } @Override public void onError(String error) { runOnUiThread(() -> Toast.makeText(context, "裁剪失败: " + error, Toast.LENGTH_SHORT).show()); } }); gisMapView.finishDraw(); // 清除临时图形 }); // 取消裁剪 Button btnClipCancel = findViewById(R.id.btn_clip_cancel); btnClipCancel.setOnClickListener(v -> { gisMapView.discardCurrentDrawing(); gisMapView.clearHighlight(); // 重置状态 }); ``` ***注意事项*** - 裁剪几何必须为面:如果传入线或点,回调会返回 onError("裁剪几何必须为面")。 - 源要素与裁剪面不相交:裁剪结果为空,回调 onError("裁剪结果为空")。 - 性能:对于复杂面(上千个顶点),JTS 计算可能需要几十到几百毫秒,但 SDK 已在后台线程执行,不会阻塞 UI。 - 坐标系处理:SDK 内部会自动将用户绘制的裁剪面(WGS84)转换为数据库存储的 CGCS2000 坐标系,无需额外转换。 - 原要素被删除:裁剪成功后原要素会被删除,新要素替代它。如需保留原要素,请先复制一份再裁剪。 - 不生成多要素:如果裁剪结果是多个分离的部分(例如一个面被切成两块),SDK 目前只返回第一个部分(主几何)。若需要生成多个要素,可在回调中进一步拆分。 ***常见问题*** |问题现象| 可能原因| 解决方法| |--------|---------|-------| |裁剪后新要素没有出现| 坐标系转换失败或裁剪面与要素不相交| 检查裁剪面是否完全或部分覆盖源要素,确保绘制正确 |裁剪成功但提示索引错误| 回调中错误使用了 clippedFeature.id() 或未正确获取要素| 更新 SDK 或回调中避免依赖 clippedFeature 的具体属性 |无法启动裁剪| 未先选中要素| 先点击要素使其高亮 |裁剪后地图未刷新| 未调用 refreshGeoPackage()| 在 onSuccess 中调用 gisMapView.refreshGeoPackage() ### 修边(Trim)功能 修边(Trim)是 GIS 中对线或面要素进行切割的功能,使用一条分割线将要素分割成若干子要素。与分割(Split)不同,修边提供了更智能的结果处理: - 如果分割后恰好产生 2 个子要素,SDK 自动保留长度较长(线)或面积较大(面)的那一个,原要素被删除,新要素插入数据库。 - 如果分割后产生 3 个及以上子要素,SDK 不自动保存,而是将子要素列表返回给上层 App,由 App 自行决定保留哪些(可弹出选择对话框)。 ***功能特性*** - 支持分割线要素和面要素。 - 分割线可以是用户在地图上绘制的任意线(点状线或自由线)。 - 自动处理坐标系转换(WGS84 ↔ CGCS2000)。 - 异步执行,不阻塞 UI。 ***交互流程(推荐 UI 实现)*** - 选中要素:用户在地图上点击线或面要素,SDK 高亮显示。 - 启动修边:点击“修边”按钮,SDK 进入分割线绘制模式(可调用 startDrawLineByPoint() 或 startDrawFreehandLine())。 - 绘制分割线:用户在地图上画一条与目标要素相交的线。 - 完成并执行:用户点击“完成”按钮,SDK 获取绘制的线,调用 trimFeature 方法。 - 结果处理: - 若分割成 2 个部分,SDK 自动保留较大的部分,地图刷新,新要素显示。 - 若分割成 3 个及以上部分,SDK 弹出子要素列表供用户选择保留哪些(需要 App 实现对话框逻辑)。 ***公开 API*** ```java /** * 修边(用分割线分割线/面要素) * @param feature 要修边的要素(DPPFeature,线或面) * @param splitLine 分割线(DPPPolyline,WGS84坐标系) * @param callback 回调,包含自动保留或返回多要素列表 */ public void trimFeature(DPPFeature feature, DPPPolyline splitLine, TrimCallback callback) ``` ***TrimCallback 接口:*** ```java public interface TrimCallback { /** * 分割结果恰好为2个,自动保留较大的一个并已入库 * @param retainedFeature 保留的要素 */ void onTrimAuto(DPPFeature retainedFeature); /** * 分割结果≥3个,返回所有子要素列表(未入库) * @param subFeatures 子要素列表 */ void onTrimMulti(List subFeatures); void onError(String error); } ``` ***使用示例(编程方式)*** ```java // 假设已通过地图点击获取到要素 lineFeature 或 polygonFeature DPPPolyline splitLine = new DPPPolyline(Arrays.asList( new DPPPoint(106.7134, 26.5775), new DPPPoint(106.7200, 26.5800) )); gisMapView.trimFeature(selectedFeature, splitLine, new TrimCallback() { @Override public void onTrimAuto(DPPFeature retainedFeature) { runOnUiThread(() -> { Toast.makeText(context, "修边成功,保留 ID=" + retainedFeature.id(), Toast.LENGTH_SHORT).show(); gisMapView.refreshGeoPackage(); gisMapView.clearHighlight(); }); } @Override public void onTrimMulti(List subFeatures) { // 弹出选择对话框让用户选择保留哪些 String[] names = new String[subFeatures.size()]; for (int i = 0; i < subFeatures.size(); i++) { names[i] = "子要素 " + (i + 1); } new AlertDialog.Builder(context) .setTitle("选择要保留的要素") .setItems(names, (dialog, which) -> { DPPFeature selected = subFeatures.get(which); // 插入选中的要素到数据库 gisMapView.insertFeature(selected.tableName(), selected.properties(), selected.geometry(), new InsertCallback() { @Override public void onSuccess(long featureId) { Toast.makeText(context, "已保留,ID=" + featureId, Toast.LENGTH_SHORT).show(); // 删除原要素 gisMapView.deleteFeature(selected.tableName(), selectedFeature.id(), new OperationCallback() { @Override public void onSuccess() { gisMapView.refreshGeoPackage(); gisMapView.clearHighlight(); } @Override public void onError(String error) { Toast.makeText(context, "删除原要素失败: " + error, Toast.LENGTH_SHORT).show(); } }); } @Override public void onError(String error) { Toast.makeText(context, "插入失败: " + error, Toast.LENGTH_SHORT).show(); } }); }) .setNegativeButton("取消", null) .show(); } @Override public void onError(String error) { runOnUiThread(() -> Toast.makeText(context, "修边失败: " + error, Toast.LENGTH_SHORT).show()); } }); ``` ***注意事项*** - 分割线必须与目标要素相交,否则会返回 onError("分割线与线无交点") 或类似错误。 - 线分割时,分割线与线的交点只取第一个,将线分成两段;若多个交点,仅第一个交点有效。 - 面分割时,分割线必须贯穿面(两个交点以上),才能有效分割。 - 性能:对于复杂多边形,切割运算可能耗时,但 SDK 在后台线程执行,不阻塞 UI。 - 子要素处理:当 onTrimMulti 回调时,子要素的 id 为 -1,尚未入库。App 可选择插入一个或多个,并自行删除原要素。 ***与分割(Split)的区别*** |功能 |分割(Split) |修边(Trim)| |------|------|------| |结果处理| 返回所有子要素列表,由 App 自行处理| 2个时自动保留较大者;≥3时返回列表 |适用场景| 需要完全控制分割结果| 常见修边需求(只保留较大块) |是否需要删除原要素| 需要 App 自行处理| SDK 自动删除原要素(2个时) ### 几何缓冲区(Buffer)功能说明 缓冲区分析用于创建围绕点、线、面要素的指定距离区域(缓冲区多边形)。SDK 支持异步生成缓冲区几何,并可通过临时高亮图层预览结果,也可自行保存为要素。 **功能特性** - 支持点、线、面要素生成缓冲区。 - 缓冲区距离单位为**米**。 - 返回 `DPPGeometry` 几何对象,不自动入库,便于预览或二次处理。 - 异步计算,不阻塞 UI。 - 自动处理坐标系(计算基于 CGCS2000,结果可通过临时图层转换为 WGS84 显示)。 **公开 API** ```java /** * 为要素创建缓冲区(不保存到数据库,仅返回几何对象) * @param sourceFeature 源要素(DPPFeature) * @param distanceMeters 缓冲距离(米) * @param callback 回调,返回缓冲后的几何 */ public void createBuffer(DPPFeature sourceFeature, double distanceMeters, BufferCallback callback) ``` **BufferCallback 接口:** ```java public interface BufferCallback { void onSuccess(DPPGeometry bufferGeometry); void onError(String error); } ``` **使用示例(创建缓冲区并用临时图层高亮显示)** ```java gisMapView.createBuffer(selectedFeature, 100.0, new BufferCallback() { @Override public void onSuccess(DPPGeometry bufferGeometry) { runOnUiThread(() -> { // 设置高亮样式(黄色填充,红色边框,线宽3) Map attrs = new HashMap<>(); attrs.put("fillColor", "#FFFF00"); attrs.put("fillOpacity", 0.3f); attrs.put("strokeColor", "#FF0000"); attrs.put("strokeWidth", 3f); gisMapView.showTemporaryGeometry(bufferGeometry, attrs); Toast.makeText(context, "缓冲区已生成,黄色区域为缓冲区", Toast.LENGTH_SHORT).show(); }); } @Override public void onError(String error) { Toast.makeText(context, "缓冲失败: " + error, Toast.LENGTH_SHORT).show(); } }); ``` **将缓冲区保存为要素** 若需要将缓冲区持久化到数据库,可在 onSuccess 回调中手动插入: ```java gisMapView.insertFeature("polygons", attrs, bufferGeometry, new InsertCallback() { @Override public void onSuccess(long featureId) { Toast.makeText(context, "缓冲区已保存,ID=" + featureId, Toast.LENGTH_SHORT).show(); gisMapView.refreshGeoPackage(); } @Override public void onError(String error) { Toast.makeText(context, "保存失败: " + error, Toast.LENGTH_SHORT).show(); } }); ``` **注意事项** - 缓冲区计算基于 CGCS2000 坐标系,结果几何也在此坐标系下。临时显示时需先转换为 WGS84(showTemporaryGeometry 已自动转换)。 - 对于大范围缓冲(如数公里),由于使用近似方法(固定纬度),精度可能存在微小偏差。如需高精度,可自行投影到适合的投影坐标系。 - 缓冲区结果可能为 MultiPolygon(例如环绕岛屿的缓冲),SDK 支持多面几何的显示和保存。 ### 相交分析(Overlay)功能说明 相交分析是 GIS 中基于几何集合运算的重要工具,用于对两个要素进行空间叠加,生成新的几何结果。SDK 支持四种运算:相交(Intersection)、合并(Union)、差集(Difference) 和 对称差(SymDifference)。 **功能特性** - 支持点、线、面任意几何类型的叠加分析(结果几何类型取决于运算规则)。 - 返回 DPPGeometry 对象,不自动入库,便于预览或二次处理。 - 异步执行,不阻塞 UI。 - 自动处理坐标系(输入、运算、输出均基于 CGCS2000,结果可通过临时图层转换为 WGS84 显示)。 **运算说明** |运算 |方法 |描述| |------|------|-----| |`相交(Intersection) intersectFeatures(A, B, callback)`| 返回同时属于 A 和 B 的几何部分。 |`合并(Union) unionFeatures(A, B, callback)` |返回属于 A 或 B 的所有几何部分的并集。 |`差集(Difference) differenceFeatures(A, B, callback)` |返回属于 A 但不属于 B 的几何部分。 |`对称差(SymDifference) symDifferenceFeatures(A, B, callback)` |返回属于 A 或 B 但不同时属于两者的几何部分(即 (A ∪ B) - (A ∩ B))。 **公开 API** ```java // 相交 void intersectFeatures(DPPFeature featureA, DPPFeature featureB, OverlayCallback callback); // 合并 void unionFeatures(DPPFeature featureA, DPPFeature featureB, OverlayCallback callback); // 差集 void differenceFeatures(DPPFeature featureA, DPPFeature featureB, OverlayCallback callback); // 对称差 void symDifferenceFeatures(DPPFeature featureA, DPPFeature featureB, OverlayCallback callback); ``` **OverlayCallback 接口:** ```java public interface OverlayCallback { void onSuccess(DPPGeometry resultGeometry); void onError(String error); } ``` **使用示例** ```java Button btnIntersect = findViewById(R.id.btn_intersect); btnIntersect.setOnClickListener(v -> { // 假设已有线要素 lineFeature 和面要素 polygonFeature gisMapView.getIntersectionPoints(lineFeature, polygonFeature, new OnIntersectionPointsListener() { @Override public void onSuccess(List points) { runOnUiThread(()->{ Toast.makeText(MainActivity.this, "交点:"+points.size(), Toast.LENGTH_SHORT).show(); }); if (points.size() < 2) { Toast.makeText(MainActivity.this, "交点少于2个,无法进行分割/修边", Toast.LENGTH_SHORT).show(); return; } // 交点足够,继续执行分割或修边操作 // 例如:gisMapView.splitFeature(polygonFeature, splitLine, splitCallback); } @Override public void onError(String error) { Toast.makeText(MainActivity.this, "获取交点失败: " + error, Toast.LENGTH_SHORT).show(); } }); ``` ### 尖角检测(Sharp Angle Detection) 尖角检测功能用于自动识别多边形中内角小于等于指定阈值(例如 30°)的顶点(即“尖角”)。该功能可用于数据质量检查、多边形简化、特征点提取或警示用户调整图形。 **功能特性** - 持普通多边形(可含内环)。 - 自动计算每个顶点的内角(基于向量夹角)。 - 返回内角 ≤ 阈值的所有顶点坐标。 - 支持 WGS84 和 CGCS2000 坐标系(内部使用平面几何计算,适用于中小尺度图形)。 **公开 API** ```java /** * 异步获取指定多边形要素中所有尖角顶点 * @param polygonFeature 要检测的多边形要素(DPPFeature) * @param maxAngleDegrees 角度阈值(度数),例如 30 * @param listener 回调,返回尖角点列表(DPPPoint,WGS84 坐标) */ public void getSharpAngles(DPPFeature polygonFeature, double maxAngleDegrees, OnSharpAnglesListener listener) /** * 同步获取内存多边形的尖角顶点列表(WGS84 坐标) * @param polygon 多边形对象 * @param maxAngleDegrees 角度阈值(度数),例如 30 * @return 尖角点列表(DPPPoint),若多边形无效或转换失败则返回空列表 */ public List getSharpAngles(DPPPolygon polygon, double maxAngleDegrees) ``` **回调接口** ```java public interface OnSharpAnglesListener { void onSuccess(List sharpPoints); void onError(String error); } ``` **使用示例** ```java // 异步检测要素尖角 gisMapView.getSharpAngles(polygonFeature, 30.0, new OnSharpAnglesListener() { @Override public void onSuccess(List sharpPoints) { if (sharpPoints.isEmpty()) { Toast.makeText(context, "未检测到尖角", Toast.LENGTH_SHORT).show(); } else { GraphicsOverlay overlay = gisMapView.getGraphicsOverlay("sharp_angles"); overlay.clear(); for (DPPPoint point : sharpPoints) { Graphic graphic = new Graphic(point); graphic.setAttribute("color", "#FF0000"); graphic.setAttribute("radius", 12); overlay.addGraphic(graphic); } Toast.makeText(context, "发现 " + sharpPoints.size() + " 个尖角", Toast.LENGTH_SHORT).show(); } } @Override public void onError(String error) { Toast.makeText(context, "尖角检测失败: " + error, Toast.LENGTH_SHORT).show(); } }); // 同步检测内存多边形 DPPPolygon polygon = ...; List sharpPoints = gisMapView.getSharpAngles(polygon, 45.0); ``` **注意事项** - 角度计算基于平面几何,对于跨度过大(例如覆盖数万公里)的多边形可能存在误差,但对于一般 GIS 应用(城市、地块等)完全够用。 - 角度阈值范围应为 0~180 度。通常尖角阈值设为 30° 或 45° 即可。 - 内角小于 180° 为凸角,大于 180° 为凹角。尖角特指很小的凸角(例如锐角)。 - 返回的顶点坐标已自动转换为 WGS84,可直接用于地图显示或二次处理。 - 如需连续检测多个多边形,建议每次调用前清除旧的高亮图形(调用 overlay.clear())。 ### 自相交检测(Self-Intersection Detection) 自相交检测用于判断几何体(点、线、面)是否存在自相交或自我重叠的拓扑错误。自相交是 GIS 数据中常见的无效性错误,会导致后续空间分析(如裁剪、合并、缓冲、面积计算等)结果异常或崩溃。本功能提供快速检测,并可提取所有自相交点坐标,便于用户定位和修复。 **功能特性** - 支持所有几何类型:点、线、面、多部件(MultiPoint、MultiLineString、MultiPolygon)。 - 检测几何体是否自相交(包括线自交、多边形边界自交、共线重叠等)。 - 提供同步和异步两种方式,兼容内存几何和数据库要素。 - 可返回所有自相交点的坐标列表,用于高亮显示或自动修复。 **公开 API** ```java // ===== 判断是否存在自相交 ===== /** * 同步判断内存几何对象是否自相交(基于 JTS 的 isSimple 方法) * @param geometry 几何对象(点、线、面、多部件均可) * @return true 表示几何自相交,false 表示无自相交 */ public boolean isSelfIntersecting(DPPGeometry geometry) /** * 同步判断指定要素是否自相交(从数据库读取几何) * @param feature 要素对象 * @return true 表示要素几何自相交,false 表示无自相交或获取失败 */ public boolean isSelfIntersecting(DPPFeature feature) /** * 异步判断指定要素是否自相交 * @param feature 要素对象 * @param callback 回调(boolean 结果:true=自相交,false=非自相交或错误) */ public void isSelfIntersecting(DPPFeature feature, BooleanCallback callback) // ===== 获取自相交点列表 ===== /** * 同步获取内存几何体的所有自相交点(WGS84 坐标) * @param geometry 几何对象 * @return 自相交点列表(DPPPoint),若无则返回空列表 */ public List getSelfIntersectionPoints(DPPGeometry geometry) /** * 同步获取要素的所有自相交点(从数据库读取几何) * @param feature 要素对象 * @return 自相交点列表,若无或获取失败则返回空列表 */ public List getSelfIntersectionPoints(DPPFeature feature) /** * 异步获取要素的自相交点列表 * @param feature 要素对象 * @param listener 回调,返回 DPPPoint 列表(失败时返回错误信息) */ public void getSelfIntersectionPoints(DPPFeature feature, OnIntersectionPointsListener listener) ``` **回调接口** 自相交检测复用已有的回调接口: - BooleanCallback 用于 isSelfIntersecting 异步方法。 - OnIntersectionPointsListener 用于获取自相交点列表。 **使用示例** ```java // 1. 同步判断内存几何是否自相交 DPPPolygon polygon = ...; if (gisMapView.isSelfIntersecting(polygon)) { Log.w("Geometry", "多边形存在自相交!"); } // 2. 同步判断要素是否自相交(从数据库读取) DPPFeature feature = ...; if (gisMapView.isSelfIntersecting(feature)) { // 进行修复或提示用户 } // 3. 异步判断自相交 gisMapView.isSelfIntersecting(feature, new BooleanCallback() { @Override public void onResult(boolean result) { if (result) { Toast.makeText(context, "该要素存在自相交", Toast.LENGTH_SHORT).show(); } else { Toast.makeText(context, "几何有效,无自相交", Toast.LENGTH_SHORT).show(); } } @Override public void onError(String error) { Toast.makeText(context, "检测失败: " + error, Toast.LENGTH_SHORT).show(); } }); // 4. 获取自相交点(同步) List intersectPoints = gisMapView.getSelfIntersectionPoints(feature); for (DPPPoint p : intersectPoints) { Log.d("Point", "自相交点: " + p.getX() + ", " + p.getY()); } // 5. 获取自相交点(异步,并高亮显示) gisMapView.getSelfIntersectionPoints(feature, new OnIntersectionPointsListener() { @Override public void onSuccess(List points) { GraphicsOverlay overlay = gisMapView.getGraphicsOverlay("self_intersections"); overlay.clear(); for (DPPPoint p : points) { Graphic graphic = new Graphic(p); graphic.setAttribute("color", "#FF00FF"); graphic.setAttribute("radius", 10); overlay.addGraphic(graphic); } Toast.makeText(context, "发现 " + points.size() + " 个自相交点", Toast.LENGTH_SHORT).show(); } @Override public void onError(String error) { Toast.makeText(context, "自相交点获取失败: " + error, Toast.LENGTH_SHORT).show(); } }); ``` **注意事项** - 自相交检测基于 JTS 的 isSimple() 方法,对于多边形,会检测边界是否自交或相邻边重叠。若需更严格的拓扑有效性检查(如内环是否完全在外环内),可使用 isValid() 自行扩展。 - 提取自相交点会遍历几何体的所有线段,对于复杂多部件几何(如含大量顶点的 MultiPolygon)可能会消耗较多计算资源,建议在后台线程调用(使用异步方法)。 - 返回的自相交点坐标已自动转换为 WGS84,可直接用于地图显示。 - 若几何体不存在自相交,getSelfIntersectionPoints 返回空列表(非 null)。 - 对于面要素,正常的拐角(凸角或凹角)不会被误判为自相交点,只有非相邻边的交叉或共线重叠才会被记录。 - 如需修复自相交,可尝试使用 geometry.buffer(0)(JTS 提供),但修复后的几何可能与原几何有偏差,需评估是否可接受。 ### 空间关系判断 除了几何叠加运算(相交、合并、差集等),SDK 还提供了直接判断两个要素之间空间关系的方法,基于 JTS 拓扑运算。您可以快速判断两个矢量要素(点、线、面)之间的包含、相交、重叠等关系。 **支持的关系类型** |关系| 说明| 同步方法| 异步方法(带回调)| |----|----|-----------|-------------------| |包含 (Contains)| 要素 A 是否完全包含要素 B(B 的所有点都在 A 内部)| `contains(A, B)` |`contains(A, B, callback)` |在内部 (Within)| 要素 A 是否完全在要素 B 内部| `within(A, B)` | `within(A, B, callback)` |拓扑相等 (Equals)| 两个要素的几何形状拓扑相等(内部和边界相同)| `equals(A, B)`| `equals(A, B, callback)` |部分重叠 (Overlaps)| 两个要素部分重叠,且交集与两者均不同(适用于同维几何)| `overlaps(A, B)` |`overlaps(A, B, callback)` |相交 (Intersects)| 两个要素是否有公共点(包括边界接触)| `intersects(A, B)`| `intersects(A, B, callback)` **API 说明** 所有方法均提供同步(直接返回 boolean)和异步(通过回调返回结果)两种版本。同步版本在 UI 线程调用时会进行数据库读取(可能耗时),建议在工作线程中使用;异步版本自动在后台线程执行,适合 UI 线程调用。 **回调接口 SpatialRelationCallback** ```java public interface SpatialRelationCallback { void onResult(boolean result); void onError(String error); } ``` **同步方法(需要自行处理线程)** ```java boolean contains(DPPFeature featureA, DPPFeature featureB); boolean within(DPPFeature featureA, DPPFeature featureB); boolean equals(DPPFeature featureA, DPPFeature featureB); boolean overlaps(DPPFeature featureA, DPPFeature featureB); boolean intersects(DPPFeature featureA, DPPFeature featureB); ``` **异步方法(推荐)** ```java void contains(DPPFeature featureA, DPPFeature featureB, SpatialRelationCallback callback); void within(DPPFeature featureA, DPPFeature featureB, SpatialRelationCallback callback); void equals(DPPFeature featureA, DPPFeature featureB, SpatialRelationCallback callback); void overlaps(DPPFeature featureA, DPPFeature featureB, SpatialRelationCallback callback); void intersects(DPPFeature featureA, DPPFeature featureB, SpatialRelationCallback callback); ``` **使用示例** ***1. 同步判断(工作线程)*** ```java new Thread(() -> { boolean result = gisMapView.contains(featureA, featureB); runOnUiThread(() -> { Toast.makeText(context, result ? "A 包含 B" : "A 不包含 B", Toast.LENGTH_SHORT).show(); }); }).start(); ``` ***2. 异步判断(UI 线程安全)*** ```java gisMapView.contains(featureA, featureB, new SpatialRelationCallback() { @Override public void onResult(boolean result) { if (result) { Toast.makeText(context, "A 包含 B", Toast.LENGTH_SHORT).show(); } else { Toast.makeText(context, "A 不包含 B", Toast.LENGTH_SHORT).show(); } } @Override public void onError(String error) { Toast.makeText(context, "判断失败: " + error, Toast.LENGTH_SHORT).show(); } }); ``` **注意事项** - 坐标系:判断基于数据库存储的 CGCS2000 坐标系,保证几何精度。如需显示 WGS84 坐标,不影响关系判断。 - 几何有效性:如果要素几何无效(自相交、退化等),JTS 的判断结果可能不可靠。建议使用前调用 isValid() 检查。 - 性能:同步方法会直接读取数据库并执行 JTS 运算,对于复杂多边形可能耗时(通常 < 50ms),建议在后台线程调用。异步方法自动处理线程,推荐在 UI 线程使用。 - 关系定义: - contains:A 包含 B,要求 B 的所有点都在 A 内部,且 A 的边界不与 B 相交(可接触?实际 JTS 允许边界共享,但视为不包含)。 - within 是 contains 的反关系。 - equals 要求几何拓扑相等(点集相同),与几何对象的具体坐标精度有关(容差 1e-6)。 - overlaps 适用于两个同维几何(面-面、线-线、点-点),且交集不是它们自身。 - intersects 是最宽松的关系,只要有一点公共即为 true。 - 空几何:如果任一要素几何为 null 或为空,所有方法返回 false(同步)或回调 onResult(false)。 **结合相交分析的典型场景** - 判断包含关系:可用来筛选完全在一个地块内的子地块。 - 判断相交:用于冲突检测(例如新建道路是否与现有要素相交)。 - 判断相等:检测重复要素。 ### 几何图形简单性判断 简单图形(Simple Geometry)是指几何对象不自交(自重叠)且拓扑简单。对于点、线、面几何,SDK 提供了判断方法,可检测要素是否为简单图形。该功能常用于数据质量检查,避免自交图形导致后续分析错误。 **定义说明** - 点:始终简单。 - 线:不自交(即除端点外,线段之间不相交)即为简单。 - 面:边界(外环和内环)不自交,且内环不接触外环或彼此(JTS 中 isSimple() 会检查这些条件)。 SDK 提供了判断方法,可检测矢量要素(点/线/面)或任意 DPPGeometry 对象是否为简单图形。 > 注意:简单图形 ≠ 有效图形(Valid Geometry)。例如自交线串 isSimple() = false,但 isValid() = true;自交多边形则两者均为 false。 **API 列表** |方法(同步)| 说明| |---------|-------| |`isSimple(DPPFeature feature)` |判断数据库中的要素是否为简单图形(可能耗时,建议工作线程调用) |`isSimpleGeometry(DPPGeometry geometry)` |判断内存中的几何对象是否为简单图形 |方法(异步)| 说明| |`isSimple(DPPFeature feature, BooleanCallback callback)` |异步判断,结果通过回调返回,自动在后台执行 **回调接口:** ```java public interface BooleanCallback { void onResult(boolean result); void onError(String error); } ``` **使用示例** ***1. 同步判断(工作线程)*** ```java new Thread(() -> { boolean simple = gisMapView.isSimple(polygonFeature); runOnUiThread(() -> { Toast.makeText(context, simple ? "图形简单" : "图形复杂(存在自交)", Toast.LENGTH_SHORT).show(); }); }).start(); ``` ***2. 异步判断(UI 线程安全)*** ```java gisMapView.isSimple(polygonFeature, new BooleanCallback() { @Override public void onResult(boolean result) { if (result) { // 图形简单,可继续操作 } else { Toast.makeText(context, "图形自交,请修正后再进行分割/合并等操作", Toast.LENGTH_LONG).show(); } } @Override public void onError(String error) { Toast.makeText(context, "判断失败: " + error, Toast.LENGTH_SHORT).show(); } }); ``` ***3. 判断内存中的几何*** ```java DPPPolygon polygon = new DPPPolygon(points); boolean simple = gisMapView.isSimpleGeometry(polygon); ``` **注意事项** - 同步方法会直接读取数据库并执行 JTS 运算,对于复杂多边形(上万个顶点)可能耗时数十毫秒,建议在后台线程调用。 - 异步方法自动在后台执行,推荐在 UI 线程使用,不会阻塞界面。 - isSimple() 只能判断拓扑简单性,不检测其他有效性条件(如多边形壳方向、内环在外环外等)。如需全面有效性检查,可结合 JTS 的 isValid() 方法(SDK 后续版本将提供)。 - 对于自交线串,isSimple() 返回 false,但仍可正常显示和编辑,但某些空间分析(如缓冲区、裁剪)可能产生异常结果。建议在关键操作前预先检查。 - 空几何或 null 几何返回 false。 **典型应用场景** - 数据导入时验证:确保 GeoPackage 中的要素拓扑简单,提前发现问题。 - 编辑后检查:用户使用编辑工具修改顶点后,调用判断看是否引入了自交。 - 分割/合并前预检:避免因几何复杂导致运算失败。 ### 空间分析工具 #### 一、通用说明 - **坐标系:** 所有输入/输出坐标均为 WGS84 (EPSG:4326)。分析内部会根据 CoordinateSystemConfig 自动转换到投影坐标系进行计算,确保距离、面积等度量的准确性,结果自动转回 WGS84 便于地图显示。 - **异步执行:** 所有分析均在后台线程执行,回调运行在主线程,可直接更新 UI。 - **回调接口:** 使用 `FeatureTable.QueryCallback`,`onSuccess(List)` 返回结果,onError(String) 返回错误信息。 #### 二、工具列表 |工具 |用途 |核心方法| |-----|-----|-----| |泰森多边形 |空间划分与邻近分析 |`generateVoronoi`| |等值线生成 |等高线、等温线等 |`generateContours`| |核密度分析 |点数据集聚程度 |`kernelDensityAutoGrid `/ `kernelDensityOnGrid` / `estimateBandwidth`| |反距离权重插值 (IDW) |基于距离的插值预测 |`idwInterpolate`| |自然邻域插值 |平滑插值预测 |`naturalNeighborInterpolate`| |克里金插值 |基于空间自相关的插值 |`krigingInterpolate`| #### 三、各工具详细说明 ##### 泰森多边形(Voronoi Diagram) **用途:** 将平面划分为以输入点为中心的邻近区域。每个区域内的任意点到其内部站点的距离都比到其他站点近。常用于设施服务区、定性聚类、空间权重分配等。 **关键方法:** ```java void generateVoronoi(List points, GisPoint[] clipRect, FeatureTable.QueryCallback callback) ``` |参数| 类型| 说明| |----|----|----| points| `List` |输入站点(WGS84),至少 3 个 clipRect| `GisPoint[]` |裁剪矩形,格式 [左下点, 右上点],可为 null(自动外扩 10%) callback |`FeatureTable.QueryCallback` |`回调,onSuccess` 返回 `List`,每个 Feature 的 `geometry()` 为 DPPPolygon,属性包含 `"index"`(序号) **使用示例:** ```java // 1. 准备站点数据 List sites = Arrays.asList( new DPPPoint(116.4, 39.9), new DPPPoint(116.6, 39.8), new DPPPoint(116.2, 39.7) ); // 2. 可选裁剪范围 GisPoint[] clip = { new GisPoint(116.0, 39.5), new GisPoint(117.0, 40.5) }; // 3. 生成泰森多边形 gisMapView.generateVoronoi(sites, clip, new FeatureTable.QueryCallback() { @Override public void onSuccess(List features) { GraphicsOverlay overlay = gisMapView.getGraphicsOverlay("voronoi"); overlay.clear(); for (DPPFeature f : features) { Map style = new HashMap<>(); style.put("fillColor", Color.parseColor("#88FF0000")); style.put("strokeColor", Color.BLACK); style.put("strokeWidth", 2f); overlay.addGraphic(new Graphic(f.geometry(), style)); } } @Override public void onError(String error) { Log.e("Voronoi", error); } }); ``` ##### 等值线生成(Contour Lines) **用途:** 从离散点数据生成等值线(如等高线、等温线、等压线等),用于地形或连续表面可视化。 **关键方法:** ```java void generateContours(List points, List values, double[] contourLevels, FeatureTable.QueryCallback callback) ``` |参数| 类型| 说明| |----|----|----| points| `List` |离散点(WGS84) values| `List` |对应点的属性值(如高程、温度) contourLevels| `double[]` |要提取的等值线级别数组(升序),如 {100, 200, 300} callback| `FeatureTable.QueryCallback` |回调,返回 `List`,每个 Feature 的 `geometry()` 为 DPPPolyline,属性包含 `"level"`(级别值) **使用示例:** ```java List pts = Arrays.asList( new DPPPoint(116.4, 39.9), new DPPPoint(116.6, 39.8), new DPPPoint(116.2, 39.7), new DPPPoint(116.5, 40.0) ); List elevations = Arrays.asList(120.0, 150.0, 90.0, 110.0); double[] levels = {100, 120, 140}; gisMapView.generateContours(pts, elevations, levels, new FeatureTable.QueryCallback() { @Override public void onSuccess(List features) { GraphicsOverlay overlay = gisMapView.getGraphicsOverlay("contours"); overlay.clear(); for (DPPFeature f : features) { Map style = new HashMap<>(); style.put("color", Color.parseColor("#FF6600")); style.put("width", 2f); overlay.addGraphic(new Graphic(f.geometry(), style)); } } @Override public void onError(String error) { Log.e("Contour", error); } }); ``` ##### 核密度分析(Kernel Density Estimation) **用途:** 估算点数据在空间上的聚集程度,生成密度表面。常用于犯罪热点识别、POI 聚集度分析、野生动物活动范围等。 **核心概念:** - **带宽 (bandwidth):** 控制密度估计的平滑程度,带宽越大,密度分布越平滑。单位与投影坐标系一致(通常为米)。 - **网格分辨率 (gridResolution):** 输出网格点的间距,影响结果的精细度。 **关键方法:** ```java // 方法1:自动生成网格 void kernelDensityAutoGrid(List points, double bandwidth, double gridResolution, FeatureTable.QueryCallback callback) // 方法2:指定网格点 void kernelDensityOnGrid(List points, List gridPoints, double bandwidth, FeatureTable.QueryCallback callback) // 方法3:自动估算带宽(Silverman 规则) double estimateBandwidth(List points) ``` |参数| 类型| 说明| |----|----|----| points| `List` |输入点(WGS84) gridPoints| `List` |自定义网格点(WGS84) bandwidth| `double` |带宽(投影单位,通常为米) gridResolution| `double` |网格间距(投影单位) callback| `FeatureTable.QueryCallback` |回调,返回 `List`,每个 Feature 包含 `"density"` 属性(密度值) **使用示例:** ```java List crimePoints = ...; // 犯罪点数据 // 1. 自动估算带宽 double bandwidth = gisMapView.estimateBandwidth(crimePoints); // 2. 执行密度分析 double resolution = 100; // 100米网格 gisMapView.kernelDensityAutoGrid(crimePoints, bandwidth, resolution, new FeatureTable.QueryCallback() { @Override public void onSuccess(List features) { GraphicsOverlay overlay = gisMapView.getGraphicsOverlay("density"); overlay.clear(); // 找出最大密度值(用于颜色映射) double maxDensity = 0; for (DPPFeature f : features) { double d = (double) f.getProperty("density"); if (d > maxDensity) maxDensity = d; } for (DPPFeature f : features) { double d = (double) f.getProperty("density"); double ratio = d / maxDensity; int alpha = (int) (255 * Math.min(1, ratio * 2)); int color = Color.argb(alpha, 255, 0, 0); Map style = new HashMap<>(); style.put("color", color); style.put("radius", 6f); overlay.addGraphic(new Graphic(f.geometry(), style)); } } @Override public void onError(String error) { Log.e("Density", error); } }); ``` ##### 反距离权重插值(IDW) **用途:** 根据已知点的值,通过距离加权平均预测未知位置的值。原理简单、计算高效,适用于气象、土壤、环境监测等领域的空间推演。 **关键方法:** ```java void idwInterpolate(List knownPoints, List knownValues, List targetPoints, double power, FeatureTable.QueryCallback callback) ``` |参数| 类型| 说明| |----|----|----| knownPoints |`List` |已知点(WGS84) knownValues |`List` |已知值 targetPoints |`List `|待预测点(WGS84) power |`double`| 距离幂参数(通常为 2),值越大,近点影响越大 callback |`FeatureTable.QueryCallback` |回调,返回 `List`,每个 Feature 包含 `"predicted"` 属性(预测值) **使用示例:** ```java // 1. 准备已知点(气象站温度) List stations = Arrays.asList( new DPPPoint(116.4, 39.9), new DPPPoint(116.6, 39.8), new DPPPoint(116.2, 39.7), new DPPPoint(116.5, 40.0) ); List temps = Arrays.asList(12.0, 15.5, 10.2, 13.8); // 2. 生成预测网格 List grid = AnalystUtils.createGrid(116.0, 39.5, 117.0, 40.5, 0.02); // 3. 执行 IDW 插值 gisMapView.idwInterpolate(stations, temps, grid, 2.0, new FeatureTable.QueryCallback() { @Override public void onSuccess(List features) { // 根据 predicted 值渲染点图层 for (DPPFeature f : features) { double pred = (double) f.getProperty("predicted"); // 设置颜色... } } @Override public void onError(String error) { Log.e("IDW", error); } }); ``` ##### 自然邻域插值(Natural Neighbor Interpolation) **用途:** 基于 Delaunay 三角网和 Voronoi 图的平滑插值方法,比 IDW 更平滑、更科学。适用于连续表面的精细推演(如地形、温度场等)。 **关键方法:** ```java void naturalNeighborInterpolate(List knownPoints, List knownValues, List targetPoints, FeatureTable.QueryCallback callback) ``` |参数| 类型| 说明| |----|----|----| knownPoints| `List` |已知点(WGS84) knownValues| `List` |已知值 targetPoints| `List` |待预测点(WGS84) callback |FeatureTable.QueryCallback |回调,返回 `List`,每个 Feature 包含 `"predicted"` 属性(预测值) **使用示例:** ```java gisMapView.naturalNeighborInterpolate(stations, temps, grid, new FeatureTable.QueryCallback() { @Override public void onSuccess(List features) { // 与 IDW 类似,渲染 predicted 值 } @Override public void onError(String error) { Log.e("NaturalNeighbor", error); } }); ``` ##### 克里金插值(Kriging Interpolation) **用途:** 基于空间自相关性的最优插值方法,不仅能给出预测值,还能提供估计方差(预测不确定性)。适用于精度要求较高的场景(如矿产资源评估、环境监测等)。 **关键方法:** ```java void krigingInterpolate(List knownPoints, List knownValues, List targetPoints, FeatureTable.QueryCallback callback) ``` |参数| 类型| 说明| |----|----|----| knownPoints| `List` |已知点(WGS84) knownValues| `List` |已知值 targetPoints| `List` |待预测点(WGS84) callback| FeatureTable.QueryCallback| 回调,返回 `List`,每个 Feature 包含 `"predicted"` 属性(预测值) **使用示例:** ```java gisMapView.krigingInterpolate(stations, temps, grid, new FeatureTable.QueryCallback() { @Override public void onSuccess(List features) { // 渲染 predicted 值 } @Override public void onError(String error) { Log.e("Kriging", error); } }); ``` ##### 辅助工具:AnalystUtils `AnalystUtils` 提供了一些常用的辅助方法,方便数据准备: ```java // 生成矩形范围内的均匀网格点 List grid = AnalystUtils.createGrid(minLon, minLat, maxLon, maxLat, stepDeg); // 将点列表转换到投影坐标系 List projected = AnalystUtils.toProjectedCoords(points, targetCRS); // 计算两点欧氏距离(投影坐标系下) double dist = AnalystUtils.euclideanDistance(p1, p2); ``` ##### 方法汇总表 |分析工具 |方法名 |输入 |输出| |----|----|----|----| 泰森多边形| `generateVoronoi` |点 + 裁剪框 多边形列表 等值线| `generateContours` |点 + 值 + 级别 线列表 核密度(自动网格)| `kernelDensityAutoGrid` |点 + 带宽 + 分辨率 带密度值的点列表 核密度(指定网格)| `kernelDensityOnGrid` |点 + 网格点 + 带宽 带密度值的点列表 核密度(带宽估算)| `estimateBandwidth` |点 建议带宽(double) IDW 插值| `idwInterpolate` |已知点 + 值 + 预测点 + 幂 带预测值的点列表 自然邻域插值| `naturalNeighborInterpolate` |已知点 + 值 + 预测点 带预测值的点列表 克里金插值| `krigingInterpolate` |已知点 + 值 + 预测点 带预测值的点列表 ## API 参考 (速查) |类别 |核心方法(示例)| |-----|----| 初始化 |`setOptions()`, `setCallback()` 底图 |`loadTianDiTu()`, `addWmsLayer()`, `addWmtsLayer()` 数据加载 |`loadGeoPackage()`, `loadGeoJSON()`, `loadShapefile()` FeatureTable| `insert()`, `update()`, `deleteFeature()`, `queryAll()`, `queryByField()` 样式| `setPointStyle()`, `setDynamicPointStyle()`, `setLabelStyle()` 过滤| `setLayerFilter()`, `clearLayerFilter()` 绘制| `startDrawPoint()`, `getLastDrawResult()`, `undoDraw()` 测量| `startMeasure()`, `measureDistance()`, `measureLength()` 编辑| `startEdit()`, `saveCurrentEdit()`, `undoEdit()` 移动| `startMoveFeature()`, `cancelMove()` 合并| `mergeFeatures()` 分割| `splitFeature()` 裁剪| `clipFeature()` 修边| `trimFeature()` 缓冲| `createBuffer()` 叠加| `intersectFeatures()`, `unionFeatures()` 空间关系| `contains()`, `within()`, `intersects()` 空间分析| `generateVoronoi()`, `generateContours()`, `idwInterpolate()` 高亮| `highlightFeature()`, `clearHighlight()`, `setHighlightStyle()` Callout| `showCallout()`, `dismissCallout()`, `setCalloutContentView()` 定位| `enableLocation()`, `setOnLocationChangeListener()` 视口| `animateTo()`, `zoomToGeometry()`, `setBearing()` 缓存| `setTileCacheEnabled()`, `clearTileCache()`