# dppmap **Repository Path**: gzlpsdp/dppmap ## Basic Information - **Project Name**: dppmap - **Description**: 封装整合maplibre+jts+geopackage+proj4j开源地图应用 - **Primary Language**: Android - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 0 - **Created**: 2026-04-24 - **Last Updated**: 2026-05-22 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README ## gis-map-dpp [![](https://jitpack.io/v/com.gitee.gzlpsdp/dppmap.svg)](https://jitpack.io/#com.gitee.gzlpsdp/dppmap) gis-map-dpp 是一个基于 MapLibre Android 的高性能 GIS 地图 SDK,集成了 GeoPackage、JTS 几何运算、proj4j 坐标转换等功能。提供地图显示、矢量数据管理、绘制、编辑、查询、动态样式、标注过滤等一站式能力,帮助 Android 开发者快速构建 GIS 应用。 ## 功能特性 - **地图引擎:** 基于 MapLibre,支持矢量/栅格底图、自定义瓦片(WMS/WMTS)。 - **矢量数据:** 无缝加载 .gpkg 文件,支持点、线、面图层。 - **CRUD 操作:** 对要素进行增、删、改、查。 - **绘制:** 点、线、面(点状模式 + 自由绘制模式)。 - **几何编辑:** 线/面顶点拖拽、添加、删除,保存/取消修改。 - **动态样式:** 根据属性字段值动态设置点/线/面的颜色、半径等。 - **文本标注:** 支持点、线、面的字段标注,样式可自定义。 - **要素点击 & 高亮:** 支持单个/多个要素高亮,高亮颜色可配置。 - **属性过滤:** 通过表达式按字段条件动态显示/隐藏要素(支持等值、比较、逻辑组合)。 - **图层控制:** 独立控制每个矢量图层的显隐。 - **坐标转换:** 集成 WGS84 → GCJ02 / BD09 / CGCS2000 等(CoordinateConverter类) ## 快速开始 **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:Tag' // 替换为最新版本 } ``` **2. 权限配置** 在 AndroidManifest.xml 中添加存储权限(用于读取 GeoPackage 文件): ```xml ``` **3. 布局文件** 在 Activity 布局中添加 GisMapView: ```xml ``` **初始化地图** ```java public class MainActivity extends AppCompatActivity { private GisMapView 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() .setTianDiTuKey("您的天地图KEY") // 天地图密钥 .setBaseMapType(GisMapOptions.BaseMapType.TIANDITU_IMAGE) .setCenter(26.9, 106.7) // 中心点(纬度, 经度) .setZoom(7.0f) .build(); gisMapView.setOptions(options); gisMapView.setCallback(() -> { // 地图准备就绪,开始加载数据 loadGeoPackage(); }); } } ``` ## 核心功能 **在地图加载完成后执行** ``` //在onMapReady中加载gpkg确保地图初始化完成 getGisMapView().setCallback(new GisMapCallback() { @Override public void onMapReady() { } }); ``` ### 地图配置选项(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_IMAGE) .setTianDiTuKey("your_tianditu_key") // 初始中心点(贵阳) .setCenter(26.9, 106.7) .setZoom(8.0f) // 手势 .setEnableRotateGesture(true) .setEnableTiltGesture(true) // 指北针:一直显示,放在左上角,边距20像素,自定义图标 .setCompassEnabled(true) .setCompassFadeWhenFacingNorth(false) // 关键:一直显示 .setCompassGravity(Gravity.LEFT | Gravity.TOP) .setCompassMargins(20, 20, 20, 20) .setCompassImage(R.drawable.my_compass) .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)。 - 所有配置仅在地图初始化前有效;地图加载后可通过 GisMapView 的动态方法修改(如 setCompassEnabled、setBearing 等)。 ### 加载 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(); } }); } ``` ### 绘制图形 支持点、线、面两种绘制模式:点状(单击添加点/顶点,长按完成)和 自由绘制(滑动绘制,松手完成)。 |功能| 方法| |----|----| |绘制点 |`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() //重做上一次撤销的顶点操作 ``` ***编辑交互说明:*** ***移动顶点:*** 触摸并拖动顶点的黄色圆点。 ***删除顶点:*** 单击顶点(短按)后确认对话框。 ***添加顶点:*** 在线段上单击即可插入新顶点。 ### 要素点击与高亮 ```java //点击事件 point:点击的位置坐标 gisMapView.setOnMapClickListener(new OnMapClickListener() { @Override public void onClick(DPPLatLng point) { /** * 未点击到任何内容 */ Log.d("Click","no click"); } @Override public void onVectorFeaturesClicked(DPPLatLng point,List features) { if (features.size() == 1) { // 高亮单个要素 gisMapView.highlightFeature(features.get(0)); } else if (features.size() > 1) { // 多个要素时弹出选择框 String[] names = new String[features.size()]; for (int i = 0; i < features.size(); i++) { names[i] = features.get(i).properties().get("name").getAsString(); } new AlertDialog.Builder(context) .setTitle("请选择") .setItems(names, (d, which) -> gisMapView.highlightFeature(features.get(which))) .show(); } } @Override public void onServiceFeaturesClicked(DPPLatLng point,List features) { /* WMS/WFS 结果 */ } @Override public void onError(String error) { /* 错误信息 */ } }); //长安事件 gisMapView.setOnMapLongClickListener((point, features) -> { runOnUiThread(() -> { Toast.makeText(MainActivity.this, "长按位置: " + point.getLatitude() + "," + point.getLongitude(), Toast.LENGTH_SHORT).show(); if (!features.isEmpty()) { gisMapView.highlightFeature(features.get(0)); } }); }); // 清除高亮 gisMapView.clearHighlight(); // 自定义高亮样式 gisMapView.setHighlightStyle(Color.MAGENTA, 0.5f, // 面填充色+透明度 Color.CYAN, 6f, // 线颜色+宽度 Color.YELLOW, 14f); // 点颜色+半径 ``` **临时高亮(临时图形层)** 除了高亮数据库中的要素外,SDK 还支持在地图上**临时显示**任意几何对象(点、线、面、多线、多面),例如缓冲区预览、裁剪结果预览、临时绘制的图形等。这些图形不会保存到数据库,可通过 `GraphicsOverlay` 管理。 ***使用 GraphicsOverlay 显示临时图形*** ```java // 获取或创建一个覆盖层(每个 ID 独立) GraphicsOverlay overlay = gisMapView.getGraphicsOverlay("myTempLayer"); // 清除旧内容 overlay.clear(); // 创建一个几何对象(例如缓冲结果、裁剪结果) DPPPolygon bufferGeometry = ...; // 从 createBuffer 回调获得 // 设置样式属性 Map attrs = new HashMap<>(); attrs.put("fillColor", "#FFFF00"); // 黄色填充 attrs.put("fillOpacity", 0.4f); // 填充透明度 attrs.put("strokeColor", "#FF0000"); // 红色边框 attrs.put("strokeWidth", 3f); // 边框宽度 Graphic graphic = new Graphic(bufferGeometry, attrs); overlay.addGraphic(graphic); ``` ***高亮图形*** ```java 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); //清除临时图形 gisMapView.clearTemporaryGeometry(); ``` ***支持的几何类型及样式属性*** |几何类型| 支持的属性键| |-------|---------| |点(圆点) |color(颜色值如 "#FF0000")、radius(半径像素) |点(图标)| iconId(图片ID)、iconSize(缩放比例) |线| color、width(线宽像素) |面| fillColor、fillOpacity、strokeColor、strokeWidth、dashArray(虚线数组) |多线、多面 |自动遍历所有子要素,样式继承 ***注意事项*** - 临时图形不会影响数据库中的要素,也不会影响原有高亮图层。 - 每个 GraphicsOverlay 可包含多个图形,但过多图形会生成大量地图源和图层,建议单次显示不超过 100 个。 - 显示前建议调用 clear() 清除上一次的临时图形,避免重叠。 - 如果几何来自数据库(坐标系为 CGCS2000),必须先转换为 WGS84 才能正确显示,推荐使用 showTemporaryGeometry 方法自动处理。 ### 图形样式 **静态样式(普通样式)** ***点图层样式*** ```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 实例*** 在 GisMapView 样式加载完成后(建议在 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毫秒动画 ``` ***|*** 注意:SDK 不内置定位功能,请宿主应用自行申请定位权限(ACCESS_FINE_LOCATION)并获取位置。建议在获取到有效定位后再调用上述方法,避免传入 null 值。缩放级别(zoom)可根据需要调整(通常 12~18)。 ### 定位图标 ```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 回调中)调用这些方法。 ### 地图方向与指南针 **设置地图方位角** 您可以通过编程方式旋转地图方向(0° = 正北,顺时针增加),支持无动画直接跳转或平滑动画。 | 方法 | 说明 | |------|------| | `setBearing(double bearing)` | 立即将地图旋转到指定角度(无动画) | | `animateBearing(double bearing, long durationMs)` | 带动画旋转到指定角度,时长毫秒 | ```java // 无动画旋转到 90°(正东) gisMapView.setBearing(90.0); // 带动画旋转到 180°(正南),动画时长 500ms gisMapView.animateBearing(180.0, 500); ``` ***指北针*** 地图默认会在右上角显示一个指北针(仅在用户旋转地图后短暂出现)。您可以通过 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),无需额外编码。 ***传感器实时旋转地图(电子罗盘)*** 您可以利用设备的方向传感器,使地图自动跟随设备旋转,实现“电子罗盘”效果。 ***示例:使用旋转向量传感器*** ```java public class MainActivity extends AppCompatActivity { private GisMapView 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 注销,以节省电量。 ## 高级功能 ### 分割、合并 |操作| 支持情况| 方法| |----|-----|----| |分割线 | ✅ |`gisMapView.splitFeature(line, splitLine, callback)` |分割面 | ✅ |`gisMapView.splitFeature(polygon, splitLine, callback)` |合并线 | ✅ |`gisMapView.mergeFeatures(lineList, callback)` |合并面 | ✅ |`gisMapView.mergeFeatures(polygonList, callback)` ### 服务 **一、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)) ### 测量工具使用 ***一、交互式测量(在地图上点选测量)*** 在地图界面交互式添加测量点,自动计算长度(折线)或周长+面积(闭合多边形)。 ****方法**** |方法 |说明| |----|----| |gisMapView.startMeasure()| 启动测量模式,此时在地图上单击添加测量点,长按完成测量并显示结果。 |gisMapView.stopMeasure()| 退出测量模式,清除临时图形。 ****单位设置**** 默认使用 米(m) 和 平方米(m²)。如需更改,请在启动测量模式前获取 MeasureTool 实例设置单位: ```java // 获取 MeasureTool 实例(需通过反射或 SDK 提供 getter,以下假设已暴露) MeasureTool measureTool = gisMapView.getMeasureTool(); measureTool.setLengthUnit(UnitConverter.LengthUnit.KILOMETER); measureTool.setAreaUnit(UnitConverter.AreaUnit.HECTARE); ``` ***|*** 注:若 SDK 未提供 getMeasureTool(),可先使用默认单位,后续版本会增加。 ****测量结果展示**** - 折线:显示总长度(格式:数值 + 单位) - 闭合多边形(首尾点自动闭合):显示周长和面积,实时更新。 ***二、编程测量(直接传入几何计算)*** 无需交互,直接调用 GisMapView 中提供的测量 API 进行计算。 ****方法列表**** |方法| 参数| 返回值| 说明| |----|----|----|----| |`measureDistance(p1, p2, unit)`| `DPPPoint p1, DPPPoint p2, UnitConverter.LengthUnit unit`| double| 计算两点间球面距离(指定单位) |`measureLength(polyline, unit)`| `DPPPolyline polyline, UnitConverter.LengthUnit unit`| double| 计算折线总长度 |`measureArea(polygon, unit)`| `List polygon, UnitConverter.AreaUnit unit`| double| 计算多边形面积(指定单位) ****使用示例**** ```java // 创建两个点 DPPPoint pointA = new DPPPoint(106.7134, 26.5775); DPPPoint pointB = new DPPPoint(106.7200, 26.5800); // 测量距离(单位:公里) double distanceKm = gisMapView.measureDistance(pointA, pointB, UnitConverter.LengthUnit.KILOMETER); // 测量折线长度(单位:米) DPPPolyline line = new DPPPolyline(Arrays.asList(pointA, pointB)); double lengthM = gisMapView.measureLength(line, UnitConverter.LengthUnit.METER); // 测量多边形面积(单位:公顷) List polygonPoints = Arrays.asList( new DPPPoint(106.7134, 26.5775), new DPPPoint(106.7200, 26.5800), new DPPPoint(106.7150, 26.5850) ); double areaHa = gisMapView.measureArea(polygonPoints, UnitConverter.AreaUnit.HECTARE); ``` ***三、单位说明*** ****长度单位(UnitConverter.LengthUnit)**** |枚举值| 说明| 换算关系| |----|------|-----| |METER |米 |1 m |KILOMETER |千米| 1 km = 1000 m |MILE |英里 |1 mi = 1609.344 m |FEET |英尺| 1 ft = 0.3048 m ****面积单位(UnitConverter.AreaUnit)**** |枚举值 |说明| 换算关系| |----|------|-----| |SQUARE_METER| 平方米 |1 m² |SQUARE_KILOMETER| 平方公里| 1 km² = 1,000,000 m² |HECTARE| 公顷| 1 ha = 10,000 m² |ACRE| 英亩| 1 ac ≈ 4046.86 m² ****格式化辅助**** UnitConverter 提供了静态方法可直接获得带单位的格式化字符串: ```java String lengthStr = UnitConverter.formatLength(meters, UnitConverter.LengthUnit.KILOMETER); String areaStr = UnitConverter.formatArea(sqMeters, UnitConverter.AreaUnit.HECTARE); // 输出例如: "12.34 km" "5.67 ha" ``` ***四、注意事项*** - 所有测量计算基于 WGS84 椭球,使用 Haversine 公式(距离)和球面多边形面积算法,精度适用于常规 GIS 应用。 - 交互式测量模式会占用地图触摸事件,测量完成或调用 stopMeasure() 后自动恢复地图默认交互。 - 长按完成测量时,结果会保留在屏幕底部的 TextView 中。 - 若多边形顶点未闭合,SDK 会自动闭合(首尾点相同),但最好在传入时确保顶点顺序正确(顺时针或逆时针)。 ### 要素移动(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** ***GisMapView 方法*** ```java /** * 获取指定多边形中所有尖角顶点(内角 ≤ 阈值) * @param polygonFeature 要检测的多边形要素(DPPFeature) * @param maxAngleDegrees 角度阈值(度数),例如 30 * @param listener 回调,返回尖角点列表(DPPPoint,WGS84 坐标) */ public void getSharpAngles(DPPFeature polygonFeature, double maxAngleDegrees, OnSharpAnglesListener listener) ``` ***回调接口*** ```java public interface OnSharpAnglesListener { void onSuccess(List sharpPoints); void onError(String error); } ``` ***使用示例*** ```java // 假设已有 polygonFeature 为面要素 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 高亮显示尖角位置 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(); } }); ``` **注意事项** - 角度计算基于平面几何,对于跨度过大(例如覆盖数万公里)的多边形可能存在误差,但对于一般 GIS 应用(城市、地块等)完全够用。 - 角度阈值范围应为 0~180 度。通常尖角阈值设为 30° 或 45° 即可。 - 内角小于 180° 为凸角,大于 180° 为凹角。尖角特指很小的凸角(例如锐角)。 - 返回的顶点坐标已自动转换为 WGS84,可直接用于地图显示或二次处理。 - 如需连续检测多个多边形,建议每次调用前清除旧的高亮图形(调用 overlay.clear())。 ### 空间关系判断 除了几何叠加运算(相交、合并、差集等),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 中的要素拓扑简单,提前发现问题。 - 编辑后检查:用户使用编辑工具修改顶点后,调用判断看是否引入了自交。 - 分割/合并前预检:避免因几何复杂导致运算失败。 ## 完整 CRUD 操作 SDK 提供通用的增、删、改、查方法,无需关心底层数据库细节。所有操作均为异步(带回调),同时提供同步版本(后缀 Sync,需在工作线程调用)。 **插入要素** ```java Map attrs = new HashMap<>(); attrs.put("name", "新地块"); attrs.put("area", 123.45); gisMapView.insertFeature("polygons", attrs, geometry, new InsertCallback() { @Override public void onSuccess(long featureId) { // 插入成功,featureId 为新要素 ID } @Override public void onError(String error) { // 处理错误 } }); ``` **更新要素** ```java Map updates = new HashMap<>(); updates.put("name", "新名称"); gisMapView.updateFeature("points", featureId, updates, new OperationCallback() { @Override public void onSuccess() { } @Override public void onError(String error) { } }); ``` **删除要素** ```java // 删除单个 gisMapView.deleteFeature("points", featureId, new OperationCallback() { ... }); // 删除表内全部 gisMapView.deleteAllFeatures("points", new OperationCallback() { ... }); ``` **查询要素** ```java // 查询全部 gisMapView.queryAllFeatures("points", new FeatureTable.QueryCallback() { @Override public void onSuccess(List features) { } @Override public void onError(String error) { } }); // 按 ID 查询 gisMapView.queryFeatureById("points", featureId, callback); // 矩形查询 GisPoint bottomLeft = new GisPoint(106.0, 26.0); GisPoint topRight = new GisPoint(107.0, 27.0); gisMapView.queryFeaturesByRect("points", bottomLeft, topRight, callback); // 按字段等值查询 gisMapView.queryFeatureByField("points", "name", "张三", callback); // 多条件 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); ``` **同步操作(适用于后台线程)** SDK 的所有 CRUD 操作均提供同步版本,可在工作线程中直接调用并获取结果(或抛出异常)。**注意:切勿在主线程调用,以免阻塞 UI。** ```java // 获取表对象 FeatureTable pointTable = gisMapView.getFeatureTable("points"); new Thread(() -> { try { // 同步插入 long newId = pointTable.insertSync(attributes, geometry); // 同步更新 pointTable.updateSync(newId, updates); // 同步删除 pointTable.deleteFeatureSync(newId); // 同步查询 List all = pointTable.queryAllSync(); DPPFeature feature = pointTable.queryByIdSync(newId); List byField = pointTable.queryByFieldSync("name", "张三"); } catch (Exception e) { e.printStackTrace(); } }).start(); ``` ***|*** 提示:同步方法不会自动刷新地图源(插入/删除/更新除外),若需立即刷新可手动调用 gisMapView.refreshGeoPackage()。 ## API 参考 (速查) | 类别 | 方法(异步为主,同步见 FeatureTable) | |------|----------------------------------------| | 地图初始化 | `setOptions(GisMapOptions)`,
`setCallback(GisMapCallback)` | | 底图 | `loadTianDiTu()`,
`loadCustomBaseMap(String url, String sourceId, String layerId)` | | GeoPackage | `loadGeoPackage(String path, GeoPackageCallback)`,
`getFeatureTable(String)`,
`refreshGeoPackage()` | | WMS / WMTS / WFS | `addWmsLayer(...)`, `removeWmsLayer()`, `setWmsLayerVisible()`,
`addWmtsLayer(...)`, `removeWmtsLayer()`, `wmsQuery()`, `wfsQuery()` | | 绘制 | `startDrawPoint()`, `startDrawLineByPoint()`, `startDrawFreehandLine()`,
`startDrawPolygonByPoint()`, `startDrawFreehandPolygon()`, `finishDraw()`,
`stopDraw()`, `discardCurrentDrawing()`, `getLastDrawResult()`, `undoDraw()`, `redoDraw()` | | 测量 | `startMeasure()`, `stopMeasure()`, `measureDistance()`,
`measureLength()`, `measureArea()` | | 编辑 | `startEdit(DPPFeature)`, `saveCurrentEdit()`,
`cancelCurrentEdit()`, `undoEdit()`, `redoEdit()` | | 移动 | `startMoveFeature(DPPFeature, MoveTool.OnMoveCompleteListener)`,
`cancelMove()` | | 分割 | `splitFeature(DPPFeature, DPPPolyline, SplitCallback)` | | 合并 | `mergeFeatures(List, MergeCallback)`, `startMergeLine()`,
`startMergePolygon()`, `cancelMerge()` | | 裁剪 | `clipFeature(DPPFeature, DPPGeometry, ClipCallback)` | | 修边 | `trimFeature(DPPFeature, DPPPolyline, TrimCallback)` | | 几何缓冲 | `createBuffer(DPPFeature, double, BufferCallback)` | | 空间分析 | `intersectFeatures()`, `unionFeatures()`, `differenceFeatures()`, `symDifferenceFeatures()` (叠加运算),
`contains()/within()/equals()/overlaps()/intersects()` (同步 + 异步回调,空间关系判断),`getSharpAngles()` (尖角检测),
`isSimple()` (几何简单性判断,同步 + 异步回调),`isSimpleGeometry()` | | 要素点击 & 高亮 | `setOnMapClickListener()`, `setOnMapLongClickListener()`,
`highlightFeature()`, `addHighlightFeature()`, `removeHighlightFeature()`,
`clearHighlight()`, `setHighlightStyle()` | | 临时高亮(图形层) | `getGraphicsOverlay(String)`, `showTemporaryGeometry(DPPGeometry, Map)`, `clearTemporaryGeometry()` | | 静态样式 | `setPointStyle(int color, float radius)`, `setLineStyle(int color, float width)`,
`setPolygonStyle(int fillColor, int strokeColor, float strokeWidth, Float[] dashArray)` | | 动态样式 | `setDynamicPointStyle()`, `setDynamicLineStyle()`, `setDynamicPolygonStyle()`, `clearDynamicStyle()` | | 文本标注 | `setLabelStyle(String, LabelStyle)` | | 图层过滤 | `setLayerFilter(String, Expression/Filter/field,value)`, `clearLayerFilter(String)` | | CRUD(异步) | `insertFeature()`, `updateFeature()`, `deleteFeature()`,
`deleteAllFeatures()`, `queryAllFeatures()`, `queryFeatureById()`,
`queryFeaturesByRect()`, `queryFeatureByField()`,
`queryFeaturesByFields()`, `queryFeaturesByWhere()` | | CRUD(同步,FeatureTable) | `insertSync()`, `updateSync()`, `deleteFeatureSync()`,
`deleteAllSync()`, `queryAllSync()`, `queryByIdSync()`,
`queryByRectSync()`, `queryByFieldSync()`, `queryByFieldsSync()`,
`queryByWhereSync()` | | 图层显隐 | `setVisible(boolean)` on `FeatureTable` | | 地图视口 | `setCenter()`, `setZoom()`, `setCenterAndZoom()`, `animateTo()`, `getCenter()`, `getZoom()` | | 图片标注 | `addImageMarker()`, `updateImageMarker()`, `removeImageMarker()`, `addImageToStyle()` | ***|*** 说明:所有带 Sync 后缀的方法均为同步版本,需要在工作线程中调用,且会抛出 Exception。异步版本通过回调返回结果。