[鸿蒙2025领航者闯关]HarmonyOS中开发高德地图第九篇:绑图与测距功能
本篇教程将学习如何在地图上绑制各种图形(折线、多边形、圆形),以及实现测距和面积计算功能。创建文件 :3. AMapUtils 工具类4. PolylineOptions 完整配置5. PolygonOptions 完整配置6. CircleOptions 完整配置7. 实用技巧7.1 绘制带箭头的折线7.2 绘制渐变色折线7.3 编辑已绘制的图形本篇小结本篇教程我们学习了:下一篇我们将整合所有功
·
第九篇:绑图与测距功能
本篇教程将学习如何在地图上绑制各种图形(折线、多边形、圆形),以及实现测距和面积计算功能。
学习目标
- 绑制折线(Polyline)
- 绑制多边形(Polygon)
- 绑制圆形(Circle)
- 实现测距功能
- 实现面积计算功能
1. 覆盖物类型
| 类型 | 类名 | 说明 |
|---|---|---|
| 折线 | Polyline |
连接多个点的线段 |
| 多边形 | Polygon |
封闭的多边形区域 |
| 圆形 | Circle |
以某点为圆心的圆形区域 |
| 弧线 | Arc |
圆弧 |
| 地面覆盖物 | GroundOverlay |
贴在地面上的图片 |
2. 完整代码示例
创建文件 entry/src/main/ets/pages/Demo08_Drawing.ets:
import {
AMap,
MapView,
MapViewComponent,
MapViewManager,
MapViewCreateCallback,
CameraUpdateFactory,
LatLng,
Marker,
MarkerOptions,
Polyline,
PolylineOptions,
Polygon,
PolygonOptions,
Circle,
CircleOptions,
BitmapDescriptorFactory,
AMapUtils
} from '@amap/amap_lbs_map3d';
const MAP_VIEW_NAME = 'DrawingDemo';
/**
* 绘制模式
*/
type DrawMode = 'none' | 'polyline' | 'polygon' | 'circle';
@Entry
@Component
struct Demo08_Drawing {
private mapView: MapView | undefined = undefined;
private aMap: AMap | undefined = undefined;
// 绘制的覆盖物
private polyline: Polyline | undefined = undefined;
private polygon: Polygon | undefined = undefined;
private circle: Circle | undefined = undefined;
private pointMarkers: Marker[] = [];
// 绘制的点
private drawPoints: LatLng[] = [];
private circleCenter: LatLng | undefined = undefined;
@State isMapReady: boolean = false;
@State drawMode: DrawMode = 'none';
@State info: string = '选择绘制模式,然后点击地图添加点';
@State totalDistance: number = 0;
@State totalArea: number = 0;
@State circleRadius: number = 0;
private mapViewCreateCallback: MapViewCreateCallback =
(mapview: MapView | undefined, mapViewName: string | undefined) => {
if (!mapview || mapViewName !== MAP_VIEW_NAME) return;
this.mapView = mapview;
this.mapView.onCreate();
this.mapView.getMapAsync((map: AMap) => {
this.aMap = map;
this.isMapReady = true;
// 设置初始视野
const center = new LatLng(39.909187, 116.397451);
map.moveCamera(CameraUpdateFactory.newLatLngZoom(center, 14));
// 启用控件
map.getUiSettings()?.setZoomControlsEnabled(true);
// 地图点击事件
map.setOnMapClickListener((point: LatLng) => {
this.onMapClick(point);
});
});
};
/**
* 地图点击处理
*/
private onMapClick(point: LatLng): void {
switch (this.drawMode) {
case 'polyline':
this.addPolylinePoint(point);
break;
case 'polygon':
this.addPolygonPoint(point);
break;
case 'circle':
this.handleCircleClick(point);
break;
default:
this.info = '请先选择绘制模式';
}
}
/**
* ==================== 折线绑制 ====================
*/
/**
* 添加折线点
*/
private addPolylinePoint(point: LatLng): void {
this.drawPoints.push(point);
// 添加点标记
this.addPointMarker(point, this.drawPoints.length);
// 更新折线
this.updatePolyline();
// 计算总距离
this.calculateTotalDistance();
}
/**
* 更新折线显示
*/
private updatePolyline(): void {
if (!this.aMap || this.drawPoints.length < 2) return;
// 移除旧折线
if (this.polyline) {
this.polyline.remove();
}
// 创建新折线
const options = new PolylineOptions();
options.setPoints(this.drawPoints);
options.setWidth(8);
options.setColor(0xFF2196F3); // 蓝色
options.setGeodesic(false); // 是否大地曲线
this.polyline = this.aMap.addPolyline(options);
}
/**
* 计算总距离
*/
private calculateTotalDistance(): void {
if (this.drawPoints.length < 2) {
this.totalDistance = 0;
this.info = `已添加 ${this.drawPoints.length} 个点`;
return;
}
let distance = 0;
for (let i = 0; i < this.drawPoints.length - 1; i++) {
const p1 = this.drawPoints[i];
const p2 = this.drawPoints[i + 1];
// 使用高德工具类计算两点距离
distance += AMapUtils.calculateLineDistance(p1, p2);
}
this.totalDistance = distance;
this.info = `总距离: ${this.formatDistance(distance)}\n已添加 ${this.drawPoints.length} 个点`;
}
/**
* ==================== 多边形绑制 ====================
*/
/**
* 添加多边形点
*/
private addPolygonPoint(point: LatLng): void {
this.drawPoints.push(point);
// 添加点标记
this.addPointMarker(point, this.drawPoints.length);
// 更新多边形
this.updatePolygon();
// 计算面积和周长
this.calculatePolygonMetrics();
}
/**
* 更新多边形显示
*/
private updatePolygon(): void {
if (!this.aMap || this.drawPoints.length < 3) {
// 少于3个点时显示折线
if (this.drawPoints.length >= 2) {
if (this.polyline) this.polyline.remove();
const options = new PolylineOptions();
options.setPoints(this.drawPoints);
options.setWidth(6);
options.setColor(0xFF4CAF50);
this.polyline = this.aMap?.addPolyline(options);
}
return;
}
// 移除临时折线
if (this.polyline) {
this.polyline.remove();
this.polyline = undefined;
}
// 移除旧多边形
if (this.polygon) {
this.polygon.remove();
}
// 创建新多边形
const options = new PolygonOptions();
options.setPoints(this.drawPoints);
options.setStrokeWidth(6);
options.setStrokeColor(0xFF4CAF50); // 绿色边框
options.setFillColor(0x304CAF50); // 半透明绿色填充
this.polygon = this.aMap.addPolygon(options);
}
/**
* 计算多边形面积和周长
*/
private calculatePolygonMetrics(): void {
if (this.drawPoints.length < 3) {
this.totalArea = 0;
this.info = `已添加 ${this.drawPoints.length} 个点(至少需要3个点)`;
return;
}
// 计算面积
this.totalArea = AMapUtils.calculateArea(this.drawPoints);
// 计算周长
let perimeter = 0;
for (let i = 0; i < this.drawPoints.length; i++) {
const p1 = this.drawPoints[i];
const p2 = this.drawPoints[(i + 1) % this.drawPoints.length];
perimeter += AMapUtils.calculateLineDistance(p1, p2);
}
this.totalDistance = perimeter;
this.info = `面积: ${this.formatArea(this.totalArea)}\n周长: ${this.formatDistance(perimeter)}\n已添加 ${this.drawPoints.length} 个点`;
}
/**
* ==================== 圆形绑制 ====================
*/
/**
* 处理圆形点击
*/
private handleCircleClick(point: LatLng): void {
if (!this.circleCenter) {
// 第一次点击设置圆心
this.circleCenter = point;
this.addPointMarker(point, 1);
this.info = '圆心已设置,再次点击设置半径';
} else {
// 第二次点击设置半径
const radius = AMapUtils.calculateLineDistance(this.circleCenter, point);
this.circleRadius = radius;
// 绘制圆形
this.drawCircle(this.circleCenter, radius);
// 计算面积
const area = Math.PI * radius * radius;
this.info = `圆形半径: ${this.formatDistance(radius)}\n面积: ${this.formatArea(area)}`;
}
}
/**
* 绘制圆形
*/
private drawCircle(center: LatLng, radius: number): void {
if (!this.aMap) return;
// 移除旧圆形
if (this.circle) {
this.circle.remove();
}
// 创建新圆形
const options = new CircleOptions();
options.setCenter(center);
options.setRadius(radius);
options.setStrokeWidth(6);
options.setStrokeColor(0xFFFF9800); // 橙色边框
options.setFillColor(0x30FF9800); // 半透明橙色填充
this.circle = this.aMap.addCircle(options);
}
/**
* ==================== 通用方法 ====================
*/
/**
* 添加点标记
*/
private addPointMarker(point: LatLng, index: number): void {
if (!this.aMap) return;
const options = new MarkerOptions();
options.setPosition(point);
options.setTitle(`点${index}`);
// 根据绘制模式设置不同颜色
let hue = BitmapDescriptorFactory.HUE_BLUE;
if (this.drawMode === 'polygon') {
hue = BitmapDescriptorFactory.HUE_GREEN;
} else if (this.drawMode === 'circle') {
hue = BitmapDescriptorFactory.HUE_ORANGE;
}
options.setIcon(BitmapDescriptorFactory.defaultMarker(hue));
options.setAnchor(0.5, 1.0);
options.setZIndex(20);
const marker = this.aMap.addMarker(options);
if (marker) {
this.pointMarkers.push(marker);
}
}
/**
* 清除所有绘制
*/
private clearAll(): void {
// 清除覆盖物
if (this.polyline) {
this.polyline.remove();
this.polyline = undefined;
}
if (this.polygon) {
this.polygon.remove();
this.polygon = undefined;
}
if (this.circle) {
this.circle.remove();
this.circle = undefined;
}
// 清除标记
for (const marker of this.pointMarkers) {
marker.remove();
}
this.pointMarkers = [];
// 重置数据
this.drawPoints = [];
this.circleCenter = undefined;
this.totalDistance = 0;
this.totalArea = 0;
this.circleRadius = 0;
this.info = '已清除,选择模式开始绘制';
}
/**
* 撤销最后一个点
*/
private undoLastPoint(): void {
if (this.drawPoints.length === 0) return;
// 移除最后一个点
this.drawPoints.pop();
// 移除最后一个标记
const lastMarker = this.pointMarkers.pop();
if (lastMarker) {
lastMarker.remove();
}
// 更新图形
if (this.drawMode === 'polyline') {
this.updatePolyline();
this.calculateTotalDistance();
} else if (this.drawMode === 'polygon') {
this.updatePolygon();
this.calculatePolygonMetrics();
}
}
/**
* 切换绘制模式
*/
private setDrawMode(mode: DrawMode): void {
this.clearAll();
this.drawMode = mode;
switch (mode) {
case 'polyline':
this.info = '点击地图添加折线点,测量距离';
break;
case 'polygon':
this.info = '点击地图添加多边形顶点,测量面积';
break;
case 'circle':
this.info = '第一次点击设置圆心,第二次点击设置半径';
break;
default:
this.info = '选择绘制模式';
}
}
/**
* 格式化距离
*/
private formatDistance(meters: number): string {
if (meters < 1000) {
return `${meters.toFixed(1)}米`;
}
return `${(meters / 1000).toFixed(2)}公里`;
}
/**
* 格式化面积
*/
private formatArea(sqMeters: number): string {
if (sqMeters < 10000) {
return `${sqMeters.toFixed(1)}平方米`;
}
if (sqMeters < 1000000) {
return `${(sqMeters / 10000).toFixed(2)}公顷`;
}
return `${(sqMeters / 1000000).toFixed(2)}平方公里`;
}
aboutToAppear(): void {
MapViewManager.getInstance()
.registerMapViewCreatedCallback(this.mapViewCreateCallback);
}
aboutToDisappear(): void {
this.clearAll();
MapViewManager.getInstance()
.unregisterMapViewCreatedCallback(this.mapViewCreateCallback);
if (this.mapView) {
this.mapView.onDestroy();
this.mapView = undefined;
this.aMap = undefined;
}
}
build() {
Column() {
// 标题栏
Row() {
Text('绘图与测距')
.fontSize(18)
.fontWeight(FontWeight.Bold)
.fontColor(Color.White)
}
.width('100%')
.height(50)
.padding({ left: 16 })
.backgroundColor('#E91E63')
// 模式选择
Row() {
Button('折线测距')
.fontSize(12)
.height(36)
.layoutWeight(1)
.margin({ right: 6 })
.backgroundColor(this.drawMode === 'polyline' ? '#2196F3' : '#e0e0e0')
.fontColor(this.drawMode === 'polyline' ? Color.White : '#333')
.onClick(() => this.setDrawMode('polyline'))
Button('多边形面积')
.fontSize(12)
.height(36)
.layoutWeight(1)
.margin({ right: 6 })
.backgroundColor(this.drawMode === 'polygon' ? '#4CAF50' : '#e0e0e0')
.fontColor(this.drawMode === 'polygon' ? Color.White : '#333')
.onClick(() => this.setDrawMode('polygon'))
Button('圆形')
.fontSize(12)
.height(36)
.layoutWeight(1)
.backgroundColor(this.drawMode === 'circle' ? '#FF9800' : '#e0e0e0')
.fontColor(this.drawMode === 'circle' ? Color.White : '#333')
.onClick(() => this.setDrawMode('circle'))
}
.width('100%')
.padding({ left: 12, right: 12, top: 8, bottom: 8 })
.backgroundColor('#f5f5f5')
// 地图区域
Stack() {
MapViewComponent({ mapViewName: MAP_VIEW_NAME })
.width('100%')
.height('100%')
// 信息面板
Column() {
Text(this.info)
.fontSize(12)
.fontColor('#333')
}
.padding(10)
.backgroundColor('rgba(255,255,255,0.95)')
.borderRadius(8)
.position({ x: 10, y: 10 })
.alignItems(HorizontalAlign.Start)
}
.width('100%')
.layoutWeight(1)
// 操作按钮
Row() {
Button('撤销')
.fontSize(13)
.height(40)
.layoutWeight(1)
.margin({ right: 8 })
.backgroundColor('#FF9800')
.enabled(this.drawPoints.length > 0)
.onClick(() => this.undoLastPoint())
Button('清除')
.fontSize(13)
.height(40)
.layoutWeight(1)
.backgroundColor('#F44336')
.onClick(() => this.clearAll())
}
.width('100%')
.padding(12)
.backgroundColor('#f5f5f5')
}
.width('100%')
.height('100%')
}
}
3. AMapUtils 工具类
import { AMapUtils } from '@amap/amap_lbs_map3d';
// 计算两点距离(米)
const distance = AMapUtils.calculateLineDistance(point1, point2);
// 计算多边形面积(平方米)
const area = AMapUtils.calculateArea(points);
// 判断点是否在多边形内
const isInside = AMapUtils.isPointInPolygon(point, polygon);
// 判断点是否在圆内
const isInCircle = AMapUtils.isPointInCircle(point, center, radius);
4. PolylineOptions 完整配置
const options = new PolylineOptions();
// 基本属性
options.setPoints(points); // 坐标点数组
options.setWidth(10); // 线宽(像素)
options.setColor(0xFF2196F3); // 颜色(ARGB)
// 线型设置
options.setGeodesic(true); // 是否大地曲线
options.setDottedLine(false); // 是否虚线
// 可见性
options.setVisible(true); // 是否可见
options.setZIndex(10); // 层级
// 纹理设置
options.setUseTexture(true); // 使用纹理
options.setLineTexture(texture); // 纹理图片
5. PolygonOptions 完整配置
const options = new PolygonOptions();
// 顶点
options.setPoints(points);
// 边框
options.setStrokeWidth(5);
options.setStrokeColor(0xFF4CAF50);
// 填充
options.setFillColor(0x304CAF50);
// 其他
options.setVisible(true);
options.setZIndex(10);
6. CircleOptions 完整配置
const options = new CircleOptions();
// 基本属性
options.setCenter(center); // 圆心
options.setRadius(1000); // 半径(米)
// 边框
options.setStrokeWidth(5);
options.setStrokeColor(0xFFFF9800);
// 填充
options.setFillColor(0x30FF9800);
// 其他
options.setVisible(true);
options.setZIndex(10);
7. 实用技巧
7.1 绘制带箭头的折线
// 设置箭头纹理
const arrowTexture = await BitmapDescriptorFactory.fromRawfilePath(
context,
'arrow.png'
);
options.setLineTexture(arrowTexture);
options.setUseTexture(true);
7.2 绘制渐变色折线
// 设置分段颜色
const colors = [0xFF4CAF50, 0xFFFFEB3B, 0xFFF44336];
options.setColorValues(colors);
7.3 编辑已绘制的图形
// 更新折线点
polyline.setPoints(newPoints);
// 更新多边形点
polygon.setPoints(newPoints);
// 更新圆形
circle.setCenter(newCenter);
circle.setRadius(newRadius);
本篇小结
本篇教程我们学习了:
- ✅ 折线的绘制和测距功能
- ✅ 多边形的绘制和面积计算
- ✅ 圆形的绘制
- ✅ AMapUtils工具类的使用
- ✅ 覆盖物的样式配置
下一篇我们将整合所有功能,实现一个完整的地图应用。
班级
https://developer.huawei.com/consumer/cn/training/classDetail/fd34ff9286174e848d34cde7f512ce22?type=1%3Fha_source%3Dhmosclass&ha_sourceId=89000248
源码地址
https://gitcode.com/daleishen/gaodehmjiaocheng.git
更多推荐
所有评论(0)