当前位置: 首页 > news >正文

OpenLayers地图交互 -- 章节五:捕捉交互详解

前言

在前面的文章中,我们学习了OpenLayers中绘制交互、选择交互和修改交互的应用技术。本文将深入探讨OpenLayers中捕捉交互(SnapInteraction)的应用技术,这是WebGIS开发中实现精确定位、要素对齐和提高绘制精度的核心技术。捕捉交互功能允许用户在绘制或修改要素时自动吸附到附近的要素节点、边线或特定位置,大大提高了地理数据的精度和一致性。通过合理配置捕捉参数和容差设置,我们可以为用户提供智能、精确的几何编辑体验。通过一个完整的示例,我们将详细解析捕捉交互的创建、配置和与其他交互的协同工作等关键技术。

项目结构分析

模板结构

<template><!--地图挂载dom--><div id="map"><div class="MapTool"><el-select v-model="value" placeholder="请选择" @change="drawChange"><el-optionv-for="item in options":key="item.value":label="item.label":value="item.value"></el-option></el-select></div></div>
</template>

模板结构详解:

  • 地图容器: id="map" 作为地图的唯一挂载点
  • 工具面板: .MapTool 包含绘制类型选择器,提供用户操作界面
  • 选择器组件: el-select 提供绘制类型选择功能,支持动态切换
  • 选项列表: el-option 显示可选的绘制类型(点、线、面、圆)
  • 响应式绑定: 使用v-model双向绑定选中的绘制类型
  • 事件监听: @change监听选择变化,实现绘制模式的动态切换

依赖引入详解

import {Map, View} from 'ol'
import {Draw, Select, Modify, Snap} from 'ol/interaction';
import Polygon from 'ol/geom/Polygon';
import {OSM, Vector as VectorSource} from 'ol/source';
import {Tile as TileLayer, Vector as VectorLayer} from 'ol/layer';
import {Circle as CircleStyle, Fill, Stroke, Style, Icon} from 'ol/style';
import marker from './data/marker.png'

依赖说明:

  • Map, View: OpenLayers的核心类,Map负责地图实例管理,View控制地图视图参数
  • Draw, Select, Modify, Snap: 交互类组合,实现完整的编辑工作流
    • Draw: 提供绘制功能
    • Select: 提供选择功能
    • Modify: 提供修改功能
    • Snap: 提供捕捉功能
  • Polygon: 多边形几何类,用于处理复杂几何数据
  • OSM, VectorSource: 数据源类,OSM提供基础地图,VectorSource管理矢量数据
  • TileLayer, VectorLayer: 图层类,分别显示瓦片和矢量数据
  • CircleStyle, Fill, Icon, Stroke, Style: 样式类,用于配置要素的视觉呈现
  • marker: 点要素图标资源,提供自定义点符号

属性说明表格

1. 依赖引入属性说明

属性名称

类型

说明

用途

Map

Class

地图核心类

创建和管理地图实例

View

Class

地图视图类

控制地图显示范围、投影和缩放

Draw

Class

绘制交互类

提供几何要素绘制功能

Select

Class

选择交互类

提供要素选择功能

Modify

Class

修改交互类

提供要素几何修改功能

Snap

Class

捕捉交互类

提供智能捕捉和对齐功能

Polygon

Class

多边形几何类

处理多边形几何数据

OSM

Source

OpenStreetMap数据源

提供基础地图瓦片服务

VectorSource

Class

矢量数据源类

管理矢量要素的存储和操作

TileLayer

Layer

瓦片图层类

显示栅格瓦片数据

VectorLayer

Layer

矢量图层类

显示矢量要素数据

CircleStyle

Style

圆形样式类

配置点要素的圆形显示样式

Fill

Style

填充样式类

配置要素的填充颜色和透明度

Stroke

Style

边框样式类

配置要素的边框颜色和宽度

Style

Style

样式基类

组合各种样式属性

Icon

Style

图标样式类

配置点要素的图标显示样式

2. 捕捉交互配置属性说明

属性名称

类型

默认值

说明

source

VectorSource

-

捕捉目标的矢量数据源

features

Collection

-

捕捉目标的要素集合

edge

Boolean

true

是否捕捉到边线

vertex

Boolean

true

是否捕捉到顶点

pixelTolerance

Number

10

捕捉的像素容差

wrapX

Boolean

false

是否在X轴方向环绕

3. 捕捉类型说明

捕捉类型

说明

应用场景

vertex

顶点捕捉

精确对齐到要素的控制点

edge

边线捕捉

对齐到要素的边线上任意点

center

中心点捕捉

对齐到要素的几何中心

endpoint

端点捕捉

对齐到线要素的起点或终点

midpoint

中点捕捉

对齐到线段的中点

核心代码详解

1. 数据属性初始化

data() {return {options: [{value: 'Point',label: '点'}, {value: 'LineString',label: '线'}, {value: 'Polygon',label: '面'}, {value: 'Circle',label: '圆'}],value: ''}
}

属性详解:

  • options: 绘制类型选项数组,支持多种几何类型的绘制
  • value: 当前选中的绘制类型,实现UI与功能的双向绑定
  • 几何类型支持
    • Point: 点要素,适用于标记特定位置
    • LineString: 线要素,适用于道路、边界等
    • Polygon: 面要素,适用于区域、建筑物等
    • Circle: 圆形,适用于缓冲区、影响范围等

2. 样式配置系统

// 图标样式配置
const image = new Icon({src: marker,                    // 图标资源路径anchor: [0.75, 0.5],           // 图标锚点位置rotateWithView: true,          // 是否随地图旋转
})// 几何类型样式映射
const styles = {'Point': new Style({image: image,               // 使用图标样式}),'LineString': new Style({stroke: new Stroke({color: 'green',         // 线条颜色width: 1,               // 线条宽度}),}),'Polygon': new Style({stroke: new Stroke({color: 'blue',          // 边框颜色lineDash: [4],          // 虚线样式width: 3,               // 边框宽度}),fill: new Fill({color: 'rgba(0, 0, 255, 0.1)', // 填充颜色和透明度}),}),'Circle': new Style({stroke: new Stroke({color: 'red',           // 边框颜色width: 2,               // 边框宽度}),fill: new Fill({color: 'rgba(255,0,0,0.2)', // 填充颜色和透明度}),}),
};// 样式函数
const styleFunction = function (feature) {return styles[feature.getGeometry().getType()];
};

样式配置详解:

  • 点要素样式
    • 使用自定义图标增强视觉效果
    • anchor: 控制图标的锚点位置,影响图标与坐标的对齐
    • rotateWithView: 图标随地图旋转,保持视觉一致性
  • 线要素样式
    • 绿色实线显示,清晰易识别
    • 适中的线宽,平衡视觉效果和性能
  • 面要素样式
    • 蓝色虚线边框,便于区分边界
    • 半透明填充,不遮挡底图信息
    • lineDash: 虚线模式,增强边界识别
  • 圆形要素样式
    • 红色边框和填充,突出显示
    • 适用于特殊标记和范围显示

3. 地图和图层初始化

// 创建矢量数据源
this.source = new VectorSource({wrapX: false});// 创建矢量图层
const vector = new VectorLayer({source: this.source,style: styleFunction,
});// 初始化地图
this.map = new Map({target: 'map',                  // 指定挂载domlayers: [new TileLayer({source: new OSM()       // 加载OpenStreetMap基础地图}),vector                      // 添加矢量图层],view: new View({center: [113.24981689453125, 23.126468438108688], // 视图中心位置projection: "EPSG:4326",    // 指定投影坐标系zoom: 12                    // 缩放级别})
});

初始化详解:

  • 矢量数据源配置
    • wrapX: false: 禁用X轴环绕,避免跨日期线的数据重复
    • 作为所有绘制要素的容器
  • 图层组织
    • 底层:OSM瓦片图层提供基础地图
    • 顶层:矢量图层显示用户绘制的要素
    • 层次分明,便于数据管理
  • 视图配置
    • 中心点设置在广州地区,适合演示
    • 使用WGS84坐标系,通用性强
    • 合适的缩放级别,平衡细节和范围

4. 交互组合配置

// 添加选择交互
const select = new Select();
this.map.addInteraction(select);// 添加修改交互
const modify = new Modify({source: this.source});
this.map.addInteraction(modify);// 添加捕捉交互
// 必须在修改和绘制交互之后添加捕捉交互
const snap = new Snap({source: this.source,            // 捕捉目标数据源// features: select.getFeatures() // 可选:仅捕捉选中的要素
});
this.map.addInteraction(snap);

交互组合详解:

  • 交互添加顺序
    • 先添加Select和Modify交互
    • 最后添加Snap交互,确保捕捉功能正常工作
    • 顺序很重要,影响交互的优先级和行为
  • 选择交互
    • 提供要素选择功能
    • 为修改操作提供目标要素
  • 修改交互
    • 允许编辑已存在的要素
    • 与捕捉交互配合,实现精确修改
  • 捕捉交互配置
    • source: 指定捕捉目标的数据源
    • 可选择性配置捕捉特定要素集合
    • 自动处理顶点和边线捕捉

5. 绘制功能实现

// 绘制类型切换方法
drawChange(type) {if (this.map) {this.map.removeInteraction(this.draw);this.addDraw(type)}
},// 添加绘制交互
addDraw(type) {if (type !== 'None') {this.draw = new Draw({source: this.source,    // 绘制到的数据源type: type,             // 绘制类型});this.map.addInteraction(this.draw);}
}

绘制功能详解:

  • 动态切换机制
    • 移除当前绘制交互,避免冲突
    • 根据用户选择添加相应的绘制交互
    • 保证同时只有一个绘制交互处于活动状态
  • 绘制配置
    • 指定目标数据源,统一管理绘制结果
    • 支持多种几何类型的绘制
    • 自动继承捕捉功能,实现精确绘制

应用场景代码演示

1. 高级捕捉配置

多源捕捉配置:

// 创建多个数据源
const roadSource = new VectorSource();      // 道路数据源
const buildingSource = new VectorSource();  // 建筑物数据源
const pointSource = new VectorSource();     // 兴趣点数据源// 配置不同类型的捕捉交互
const roadSnap = new Snap({source: roadSource,edge: true,         // 允许捕捉道路边线vertex: true,       // 允许捕捉道路节点pixelTolerance: 15  // 较大的容差,便于捕捉
});const buildingSnap = new Snap({source: buildingSource,edge: true,         // 允许捕捉建筑物边线vertex: true,       // 允许捕捉建筑物角点pixelTolerance: 10  // 中等容差
});const pointSnap = new Snap({source: pointSource,edge: false,        // 不捕捉边线vertex: true,       // 仅捕捉点要素pixelTolerance: 8   // 较小容差,精确捕捉
});// 按顺序添加捕捉交互
map.addInteraction(roadSnap);
map.addInteraction(buildingSnap);
map.addInteraction(pointSnap);

条件捕捉配置:

// 基于图层可见性的动态捕捉
const conditionalSnap = new Snap({source: function() {// 动态返回可见图层的数据源const visibleSources = [];map.getLayers().forEach(layer => {if (layer instanceof VectorLayer && layer.getVisible()) {visibleSources.push(layer.getSource());}});return visibleSources;},pixelTolerance: 12
});// 基于缩放级别的捕捉控制
const zoomBasedSnap = new Snap({source: vectorSource,pixelTolerance: function() {const zoom = map.getView().getZoom();// 根据缩放级别调整捕捉容差if (zoom > 15) return 5;      // 高缩放级别,精确捕捉else if (zoom > 12) return 10; // 中等缩放级别else return 15;               // 低缩放级别,宽松捕捉}
});

2. 智能捕捉系统

几何类型智能捕捉:

// 创建智能捕捉管理器
class SmartSnapManager {constructor(map) {this.map = map;this.snapInteractions = [];this.currentGeometryType = null;}// 根据绘制类型配置捕捉规则configureSnapForGeometry(geometryType) {// 清除现有捕捉交互this.clearSnaps();switch (geometryType) {case 'Point':// 点绘制:捕捉所有类型的顶点和边线this.addSnap({source: allFeaturesSource,vertex: true,edge: true,pixelTolerance: 10});break;case 'LineString':// 线绘制:主要捕捉端点和交叉点this.addSnap({source: pointSource,vertex: true,edge: false,pixelTolerance: 8});this.addSnap({source: lineSource,vertex: true,edge: true,pixelTolerance: 12});break;case 'Polygon':// 面绘制:捕捉边界和顶点,避免重叠this.addSnap({source: polygonSource,vertex: true,edge: true,pixelTolerance: 10});break;}this.currentGeometryType = geometryType;}addSnap(options) {const snap = new Snap(options);this.map.addInteraction(snap);this.snapInteractions.push(snap);}clearSnaps() {this.snapInteractions.forEach(snap => {this.map.removeInteraction(snap);});this.snapInteractions = [];}
}// 使用智能捕捉管理器
const smartSnap = new SmartSnapManager(map);// 绘制类型改变时自动配置捕捉
draw.on('change:type', function(event) {const geometryType = event.target.getType();smartSnap.configureSnapForGeometry(geometryType);
});

捕捉优先级管理:

// 捕捉优先级配置
const prioritySnapSystem = {// 高优先级:精确几何点highPriority: new Snap({source: criticalPointsSource,vertex: true,edge: false,pixelTolerance: 5}),// 中优先级:结构边线mediumPriority: new Snap({source: structuralLinesSource,vertex: true,edge: true,pixelTolerance: 10}),// 低优先级:一般要素lowPriority: new Snap({source: generalFeaturesSource,vertex: true,edge: true,pixelTolerance: 15})
};// 按优先级顺序添加
Object.values(prioritySnapSystem).forEach(snap => {map.addInteraction(snap);
});

3. 捕捉视觉反馈

捕捉点高亮显示:

// 创建捕捉点样式
const snapPointStyle = new Style({image: new CircleStyle({radius: 8,fill: new Fill({color: 'rgba(255, 255, 0, 0.8)'}),stroke: new Stroke({color: 'orange',width: 3})}),zIndex: 1000
});// 捕捉反馈图层
const snapFeedbackLayer = new VectorLayer({source: new VectorSource(),style: snapPointStyle,zIndex: 1000
});
map.addLayer(snapFeedbackLayer);// 监听鼠标移动显示捕捉反馈
map.on('pointermove', function(event) {const coordinate = event.coordinate;const snapResult = findNearestSnapPoint(coordinate);// 清除之前的反馈snapFeedbackLayer.getSource().clear();if (snapResult) {// 创建捕捉点要素const snapFeature = new Feature({geometry: new Point(snapResult.coordinate),snapType: snapResult.type});snapFeedbackLayer.getSource().addFeature(snapFeature);// 显示捕捉信息showSnapTooltip(snapResult);}
});

捕捉引导线:

// 捕捉引导线样式
const guideLineStyle = new Style({stroke: new Stroke({color: 'rgba(255, 165, 0, 0.8)',width: 2,lineDash: [5, 5]})
});// 显示捕捉引导线
const showSnapGuideLine = function(fromCoord, toCoord) {const guideLine = new Feature({geometry: new LineString([fromCoord, toCoord])});guideLine.setStyle(guideLineStyle);snapFeedbackLayer.getSource().addFeature(guideLine);// 自动清除引导线setTimeout(() => {snapFeedbackLayer.getSource().removeFeature(guideLine);}, 1000);
};

4. 捕捉精度控制

自适应捕捉容差:

// 自适应捕捉容差管理
class AdaptiveSnapTolerance {constructor(basePixelTolerance = 10) {this.basePixelTolerance = basePixelTolerance;this.zoomFactors = {high: 0.5,      // 高缩放级别:减小容差medium: 1.0,    // 中等缩放级别:正常容差low: 1.5        // 低缩放级别:增大容差};}// 根据缩放级别计算容差calculateTolerance(zoom) {let factor;if (zoom > 16) factor = this.zoomFactors.high;else if (zoom > 12) factor = this.zoomFactors.medium;else factor = this.zoomFactors.low;return Math.round(this.basePixelTolerance * factor);}// 根据要素密度调整容差adjustForFeatureDensity(coordinate, extent) {const features = vectorSource.getFeaturesInExtent(extent);const density = features.length / ol.extent.getArea(extent);// 高密度区域减小容差,低密度区域增大容差const densityFactor = density > 0.001 ? 0.7 : 1.3;return Math.round(this.basePixelTolerance * densityFactor);}
}// 使用自适应容差
const adaptiveTolerance = new AdaptiveSnapTolerance();// 动态更新捕捉容差
map.getView().on('change:resolution', function() {const zoom = map.getView().getZoom();const newTolerance = adaptiveTolerance.calculateTolerance(zoom);// 更新所有捕捉交互的容差map.getInteractions().forEach(interaction => {if (interaction instanceof Snap) {interaction.set('pixelTolerance', newTolerance);}});
});

精度验证系统:

// 捕捉精度验证
const validateSnapAccuracy = function(feature, snapCoordinate) {const geometry = feature.getGeometry();const coordinates = geometry.getCoordinates();// 计算捕捉点到最近要素的距离const distance = geometry.getClosestPoint ? ol.coordinate.distance(snapCoordinate, geometry.getClosestPoint(snapCoordinate)) :0;const validation = {isAccurate: distance < 0.1,  // 小于0.1米认为精确distance: distance,quality: distance < 0.1 ? 'high' : distance < 1 ? 'medium' : 'low'};return validation;
};// 捕捉质量监控
snap.on('snap', function(event) {const validation = validateSnapAccuracy(event.feature, event.coordinate);if (!validation.isAccurate) {console.warn('捕捉精度不足:', validation);showAccuracyWarning(validation);}
});

5. 批量捕捉操作

批量要素对齐:

// 批量对齐工具
class BatchAlignTool {constructor(map, targetSource) {this.map = map;this.targetSource = targetSource;this.selectedFeatures = [];}// 选择要对齐的要素selectFeaturesForAlignment() {const select = new Select({multi: true,condition: click});this.map.addInteraction(select);select.on('select', (event) => {this.selectedFeatures = event.selected;this.showAlignmentOptions();});}// 对齐到网格alignToGrid(gridSize = 10) {this.selectedFeatures.forEach(feature => {const geometry = feature.getGeometry();const alignedGeometry = this.snapGeometryToGrid(geometry, gridSize);feature.setGeometry(alignedGeometry);});}// 对齐到最近要素alignToNearestFeature() {this.selectedFeatures.forEach(feature => {const geometry = feature.getGeometry();const nearestFeature = this.findNearestFeature(feature);if (nearestFeature) {const alignedGeometry = this.snapGeometryToFeature(geometry, nearestFeature.getGeometry());feature.setGeometry(alignedGeometry);}});}// 几何对齐到网格snapGeometryToGrid(geometry, gridSize) {const type = geometry.getType();const coordinates = geometry.getCoordinates();const snapToGrid = (coord) => [Math.round(coord[0] / gridSize) * gridSize,Math.round(coord[1] / gridSize) * gridSize];let alignedCoordinates;switch (type) {case 'Point':alignedCoordinates = snapToGrid(coordinates);return new Point(alignedCoordinates);case 'LineString':alignedCoordinates = coordinates.map(snapToGrid);return new LineString(alignedCoordinates);case 'Polygon':alignedCoordinates = coordinates.map(ring => ring.map(snapToGrid));return new Polygon(alignedCoordinates);}return geometry;}
}// 使用批量对齐工具
const batchAlign = new BatchAlignTool(map, vectorSource);
batchAlign.selectFeaturesForAlignment();

最佳实践建议

1. 性能优化

捕捉性能优化:

// 优化大数据量的捕捉性能
const optimizedSnap = new Snap({source: vectorSource,pixelTolerance: 10,// 使用空间索引优化features: function(coordinate, resolution) {// 只在可见范围内搜索要素const extent = map.getView().calculateExtent();const features = vectorSource.getFeaturesInExtent(extent);// 限制搜索的要素数量return features.slice(0, 100);}
});// 分层捕捉策略
const layeredSnapStrategy = {// 首先检查最重要的图层primary: new Snap({source: primaryLayerSource,pixelTolerance: 5,vertex: true,edge: false}),// 其次检查次要图层secondary: new Snap({source: secondaryLayerSource,pixelTolerance: 10,vertex: true,edge: true})
};

内存管理:

// 捕捉交互资源管理
const cleanupSnapResources = function() {// 移除所有捕捉交互map.getInteractions().forEach(interaction => {if (interaction instanceof Snap) {map.removeInteraction(interaction);}});// 清理捕捉反馈图层if (snapFeedbackLayer) {snapFeedbackLayer.getSource().clear();map.removeLayer(snapFeedbackLayer);}// 清理事件监听器map.un('pointermove', snapMoveHandler);
};

2. 用户体验优化

捕捉状态指示:

// 捕捉状态视觉指示器
const createSnapIndicator = function() {const indicator = document.createElement('div');indicator.className = 'snap-indicator';indicator.innerHTML = `<div class="snap-status"><span class="snap-icon">📍</span><span class="snap-text">捕捉已启用</span></div>`;return indicator;
};// 动态显示捕捉信息
const showSnapInfo = function(snapResult) {const info = document.getElementById('snap-info');if (snapResult) {info.innerHTML = `<div class="snap-details"><p>捕捉类型: ${snapResult.type}</p><p>目标要素: ${snapResult.featureName}</p><p>精度: ${snapResult.accuracy.toFixed(2)}m</p></div>`;info.style.display = 'block';} else {info.style.display = 'none';}
};

操作指导系统:

// 智能操作提示
const smartTipsSystem = {showTip: function(tipType, context) {const tips = {snapEnabled: '捕捉功能已启用,鼠标靠近要素时会自动吸附',snapDisabled: '捕捉功能已禁用,可手动启用精确对齐',highAccuracy: '高精度模式:适合精确测量和CAD级绘制',lowAccuracy: '快速模式:适合草图绘制和概要规划'};this.displayTip(tips[tipType], context);},displayTip: function(message, context) {// 显示上下文相关的操作提示const tooltip = document.getElementById('operation-tooltip');tooltip.textContent = message;tooltip.style.display = 'block';// 3秒后自动隐藏setTimeout(() => {tooltip.style.display = 'none';}, 3000);}
};

3. 数据质量保证

捕捉结果验证:

// 捕捉结果质量检查
const validateSnapResult = function(originalCoord, snappedCoord, tolerance) {const distance = ol.coordinate.distance(originalCoord, snappedCoord);return {isValid: distance <= tolerance,distance: distance,improvement: distance < tolerance * 0.1 ? 'significant' : 'moderate',recommendation: distance > tolerance * 0.8 ? 'consider_manual_adjustment' : 'snap_accepted'};
};// 数据一致性检查
const checkDataConsistency = function(feature, snapTarget) {const checks = {spatialConsistency: checkSpatialRelationship(feature, snapTarget),attributeConsistency: checkAttributeCompatibility(feature, snapTarget),geometryIntegrity: checkGeometryIntegrity(feature)};return {passed: Object.values(checks).every(check => check.passed),details: checks};
};

捕捉历史记录:

// 捕捉操作历史管理
class SnapHistory {constructor(maxHistoryLength = 50) {this.history = [];this.maxHistoryLength = maxHistoryLength;}recordSnap(originalCoord, snappedCoord, feature, snapType) {const record = {timestamp: new Date(),originalCoordinate: originalCoord,snappedCoordinate: snappedCoord,targetFeature: feature.getId(),snapType: snapType,accuracy: ol.coordinate.distance(originalCoord, snappedCoord)};this.history.push(record);if (this.history.length > this.maxHistoryLength) {this.history.shift();}}getSnapStatistics() {return {totalSnaps: this.history.length,averageAccuracy: this.calculateAverageAccuracy(),snapTypeDistribution: this.getSnapTypeDistribution(),qualityScore: this.calculateQualityScore()};}calculateAverageAccuracy() {if (this.history.length === 0) return 0;const totalAccuracy = this.history.reduce((sum, record) => {return sum + record.accuracy;}, 0);return totalAccuracy / this.history.length;}
}

总结

OpenLayers的捕捉交互功能为WebGIS应用提供了强大的精确定位和对齐能力。通过合理配置捕捉参数、容差设置和视觉反馈系统,我们可以为用户提供智能、精确的几何编辑体验。本文详细介绍了捕捉交互的基础配置、高级功能实现和性能优化技巧,涵盖了从简单应用到复杂场景的完整解决方案。

通过本文的学习,您应该能够:

  1. 理解捕捉交互的核心概念:掌握顶点捕捉、边线捕捉等基本概念
  2. 配置多样化的捕捉规则:根据不同需求设置捕捉条件和容差
  3. 实现智能捕捉系统:构建自适应、多层次的捕捉机制
  4. 优化捕捉性能:处理大数据量场景下的性能问题
  5. 提供优质用户体验:通过视觉反馈和操作指导提升可用性
  6. 保证数据质量:实现捕捉结果验证和一致性检查

捕捉交互技术是构建专业级GIS编辑工具的重要基础,掌握这些技术将显著提升您的WebGIS应用的专业性和实用性。结合前面学习的绘制、选择和修改交互,您现在已经具备了构建完整地理数据编辑系统的技术能力。

http://www.dtcms.com/a/393694.html

相关文章:

  • 阿瓦隆1566HA-448T矿机深度解析:性能、效率与冷却技术
  • 平替confluence,推荐一款国产开源免费的知识管理工具 - sward
  • 【开源】基于STM32的智能垃圾桶
  • RuoYi-Cloud问题:访问https的网关地址,实际是访问http的文件服务
  • HttpClientFactory vs new HttpClient:.NET Core HTTP 客户端的正确打开方式
  • MySQL数据库(七)—— 基于主主复制与 Keepalived 非抢占模式的高可用方案
  • 如何提高Java并发编程的实战能力?
  • JavaWeb 课堂笔记 —— 17 SpringBootWeb案例 部门管理
  • java设计模式四,原型模式
  • 【NOIP 2024 T2】遗失的赋值
  • TypeScript学习笔记1
  • Android普通应用切到后台后,多长时间会被系统回收
  • 【Elasticsearch面试精讲 Day 21】地理位置搜索与空间查询
  • 【Android】View 的滑动
  • 【深度学习的优化理论】如何理解OT与欧几里得距离均值的区别
  • 【Android】Room数据库的基本使用
  • 项目:仿muduo库的高并发服务器
  • Oracle普通用户报错ORA-31603处理
  • 网络安全期末大论文
  • 23种设计模式之【工厂方法模式】-核心原理与 Java实践
  • cocos 添加背景,帧动画,贴图
  • 亚马逊云科技重磅推出 Amazon S3 Vectors:首款大规模支持原生向量的云存储服务
  • SQLite Expert:一款功能强大的SQLite管理工具
  • Python 2025:供应链安全威胁与防御实战
  • 队列+宽搜(BFS)-429.N叉树的层序遍历-力扣(LeetCode)
  • 【Linux命令从入门到精通系列指南】rm 命令详解:安全删除文件与目录的终极实战手册
  • Springboot使用dockerfile-maven-plugin部署镜像
  • 安卓蓝牙键盘和鼠标6.10.4去更新汉化版 手机变为蓝牙键盘和鼠标
  • 工作笔记-----lwip的内存管理策略解析
  • 量子计算学习笔记(1)