第15章 - 要素编辑与绘制
第15章 - 要素编辑与绘制
15.1 绘制功能
15.1.1 基本绘制
import Draw from 'ol/interaction/Draw';
import VectorSource from 'ol/source/Vector';
import VectorLayer from 'ol/layer/Vector';
import { Style, Fill, Stroke, Circle } from 'ol/style';
// 创建编辑图层
const editSource = new VectorSource();
const editLayer = new VectorLayer({
source: editSource,
style: new Style({
fill: new Fill({ color: 'rgba(66, 133, 244, 0.3)' }),
stroke: new Stroke({ color: '#4285F4', width: 2 }),
image: new Circle({
radius: 6,
fill: new Fill({ color: '#4285F4' })
})
})
});
map.addLayer(editLayer);
// 绘制点
const drawPoint = new Draw({ source: editSource, type: 'Point' });
// 绘制线
const drawLine = new Draw({ source: editSource, type: 'LineString' });
// 绘制面
const drawPolygon = new Draw({ source: editSource, type: 'Polygon' });
// 绘制圆
const drawCircle = new Draw({ source: editSource, type: 'Circle' });
15.1.2 特殊形状绘制
import { createBox, createRegularPolygon } from 'ol/interaction/Draw';
// 矩形
const drawBox = new Draw({
source: editSource,
type: 'Circle',
geometryFunction: createBox()
});
// 正方形
const drawSquare = new Draw({
source: editSource,
type: 'Circle',
geometryFunction: createRegularPolygon(4)
});
// 正六边形
const drawHexagon = new Draw({
source: editSource,
type: 'Circle',
geometryFunction: createRegularPolygon(6)
});
// 自由绘制
const freehand = new Draw({
source: editSource,
type: 'LineString',
freehand: true
});
15.1.3 绘制事件
const draw = new Draw({ source: editSource, type: 'Polygon' });
draw.on('drawstart', (event) => {
console.log('开始绘制');
});
draw.on('drawend', (event) => {
const feature = event.feature;
const area = getArea(feature.getGeometry());
console.log('绘制完成,面积:', area, '平方米');
});
draw.on('drawabort', () => {
console.log('取消绘制');
});
// Esc 键取消绘制
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape') {
draw.abortDrawing();
}
});
15.2 编辑功能
15.2.1 修改要素
import Modify from 'ol/interaction/Modify';
import Snap from 'ol/interaction/Snap';
// 修改所有要素
const modify = new Modify({ source: editSource });
// 修改选中要素
const select = new Select();
const modifySelected = new Modify({ features: select.getFeatures() });
// 捕捉
const snap = new Snap({ source: editSource });
// 添加交互(顺序重要)
map.addInteraction(select);
map.addInteraction(modifySelected);
map.addInteraction(snap);
// 事件
modify.on('modifyend', (event) => {
console.log('修改完成');
});
15.2.2 删除要素
// 删除选中要素
function deleteSelected() {
const features = select.getFeatures();
features.forEach(feature => {
editSource.removeFeature(feature);
});
features.clear();
}
// 按 Delete 键删除
document.addEventListener('keydown', (e) => {
if (e.key === 'Delete') {
deleteSelected();
}
});
// 右键菜单删除
map.on('contextmenu', (event) => {
event.preventDefault();
const feature = map.forEachFeatureAtPixel(event.pixel, f => f);
if (feature) {
editSource.removeFeature(feature);
}
});
15.2.3 平移要素
import Translate from 'ol/interaction/Translate';
// 平移选中要素
const translate = new Translate({
features: select.getFeatures()
});
translate.on('translateend', (event) => {
console.log('平移完成');
});
map.addInteraction(translate);
15.3 编辑工具栏
class EditToolbar {
constructor(map, source) {
this.map = map;
this.source = source;
this.activeInteraction = null;
// 选择和修改交互
this.select = new Select();
this.modify = new Modify({ features: this.select.getFeatures() });
this.snap = new Snap({ source });
map.addInteraction(this.select);
map.addInteraction(this.modify);
map.addInteraction(this.snap);
this.select.setActive(false);
this.modify.setActive(false);
}
activateTool(type) {
this.deactivate();
if (type === 'select') {
this.select.setActive(true);
this.modify.setActive(true);
} else if (['Point', 'LineString', 'Polygon', 'Circle'].includes(type)) {
this.activeInteraction = new Draw({
source: this.source,
type: type
});
this.map.addInteraction(this.activeInteraction);
}
}
deactivate() {
this.select.setActive(false);
this.modify.setActive(false);
if (this.activeInteraction) {
this.map.removeInteraction(this.activeInteraction);
this.activeInteraction = null;
}
}
deleteSelected() {
const features = this.select.getFeatures();
features.forEach(f => this.source.removeFeature(f));
features.clear();
}
clear() {
this.source.clear();
this.select.getFeatures().clear();
}
undo() {
// 实现撤销功能需要历史记录
}
}
// 使用
const toolbar = new EditToolbar(map, editSource);
document.getElementById('draw-point').onclick = () => toolbar.activateTool('Point');
document.getElementById('draw-line').onclick = () => toolbar.activateTool('LineString');
document.getElementById('draw-polygon').onclick = () => toolbar.activateTool('Polygon');
document.getElementById('select').onclick = () => toolbar.activateTool('select');
document.getElementById('delete').onclick = () => toolbar.deleteSelected();
document.getElementById('clear').onclick = () => toolbar.clear();
15.4 测量功能
import { getLength, getArea } from 'ol/sphere';
class MeasureTool {
constructor(map) {
this.map = map;
this.source = new VectorSource();
this.layer = new VectorLayer({
source: this.source,
style: this.createStyle()
});
map.addLayer(this.layer);
this.draw = null;
this.tooltip = null;
}
measureDistance() {
this.clear();
this.draw = new Draw({ source: this.source, type: 'LineString' });
this.draw.on('drawstart', (e) => {
e.feature.on('change', () => {
const geom = e.feature.getGeometry();
const length = getLength(geom);
this.updateTooltip(geom.getLastCoordinate(), this.formatLength(length));
});
});
this.draw.on('drawend', () => {
this.tooltip = null;
});
this.map.addInteraction(this.draw);
}
measureArea() {
this.clear();
this.draw = new Draw({ source: this.source, type: 'Polygon' });
this.draw.on('drawstart', (e) => {
e.feature.on('change', () => {
const geom = e.feature.getGeometry();
const area = getArea(geom);
const coord = geom.getInteriorPoint().getCoordinates();
this.updateTooltip(coord, this.formatArea(area));
});
});
this.map.addInteraction(this.draw);
}
formatLength(length) {
return length > 1000 ?
`${(length / 1000).toFixed(2)} km` :
`${length.toFixed(2)} m`;
}
formatArea(area) {
return area > 1000000 ?
`${(area / 1000000).toFixed(2)} km²` :
`${area.toFixed(2)} m²`;
}
createStyle() {
return new Style({
fill: new Fill({ color: 'rgba(255, 255, 255, 0.2)' }),
stroke: new Stroke({ color: '#ffcc33', width: 2 }),
image: new Circle({
radius: 5,
fill: new Fill({ color: '#ffcc33' })
})
});
}
updateTooltip(coordinate, text) {
// 创建或更新工具提示
if (!this.tooltip) {
this.tooltip = new Overlay({
element: this.createTooltipElement(),
offset: [0, -15],
positioning: 'bottom-center'
});
this.map.addOverlay(this.tooltip);
}
this.tooltip.getElement().innerHTML = text;
this.tooltip.setPosition(coordinate);
}
createTooltipElement() {
const element = document.createElement('div');
element.className = 'measure-tooltip';
return element;
}
clear() {
if (this.draw) {
this.map.removeInteraction(this.draw);
this.draw = null;
}
this.source.clear();
if (this.tooltip) {
this.map.removeOverlay(this.tooltip);
this.tooltip = null;
}
}
}
15.5 数据导入导出
import GeoJSON from 'ol/format/GeoJSON';
import KML from 'ol/format/KML';
// 导出 GeoJSON
function exportGeoJSON(source) {
const format = new GeoJSON();
const features = source.getFeatures();
const json = format.writeFeatures(features, {
dataProjection: 'EPSG:4326',
featureProjection: 'EPSG:3857'
});
const blob = new Blob([json], { type: 'application/json' });
const url = URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = 'features.geojson';
link.click();
URL.revokeObjectURL(url);
}
// 导入 GeoJSON
function importGeoJSON(source, file) {
const reader = new FileReader();
reader.onload = (e) => {
const format = new GeoJSON();
const features = format.readFeatures(e.target.result, {
dataProjection: 'EPSG:4326',
featureProjection: 'EPSG:3857'
});
source.addFeatures(features);
};
reader.readAsText(file);
}
// 文件拖放导入
map.getTargetElement().addEventListener('dragover', (e) => {
e.preventDefault();
});
map.getTargetElement().addEventListener('drop', (e) => {
e.preventDefault();
const file = e.dataTransfer.files[0];
if (file.name.endsWith('.geojson') || file.name.endsWith('.json')) {
importGeoJSON(editSource, file);
}
});
15.6 本章小结
本章介绍了要素编辑与绘制:
- 绘制功能:点、线、面、特殊形状
- 编辑功能:修改、删除、平移
- 编辑工具栏:工具管理
- 测量功能:距离、面积测量
- 数据导入导出:GeoJSON、KML
关键要点
- Draw 交互实现绘制
- Modify + Snap 实现编辑
- 使用 GeoJSON 格式导入导出

浙公网安备 33010602011771号