天地图API实战:5分钟搞定地图标注与编辑功能(附完整代码)
天地图API实战5分钟搞定地图标注与编辑功能附完整代码第一次接触天地图API时我被它简洁的文档和丰富的功能所吸引。作为一个经常需要在项目中集成地图功能的开发者我发现天地图在标注和编辑方面的API设计尤其友好。本文将带你快速实现点、线、面的绘制与编辑功能所有代码片段都可直接复制使用。1. 环境准备与基础配置在开始之前我们需要准备好开发环境。天地图API的引入非常简单只需要在HTML文件中添加一个script标签即可。不过要注意使用前需要先申请开发者密钥。!DOCTYPE html html head meta charsetutf-8 title天地图标注与编辑/title style #map { width: 100%; height: 600px; } .toolbar { margin: 10px; } .toolbar button { margin-right: 5px; } /style /head body div idmap/div div classtoolbar button onclickstartDrawing(point)画点/button button onclickstartDrawing(line)画线/button button onclickstartDrawing(polygon)画面/button button onclicktoggleEdit()编辑/button button onclickclearAll()清除/button /div script srchttps://api.tianditu.gov.cn/api?v4.0tk你的密钥/script script srcapp.js/script /body /html在app.js中我们先初始化地图对象// 初始化地图 const map new T.Map(map); // 设置中心点和缩放级别 map.centerAndZoom(new T.LngLat(116.404, 39.915), 12);提示在实际项目中建议将密钥存储在环境变量中不要直接硬编码在HTML文件里。2. 实现点标注功能点标注是地图应用中最基础的功能。天地图提供了T.Marker类来创建标记点我们可以通过简单的几行代码实现点的添加和交互。// 存储所有标记点 let markers []; // 添加标记点 function addMarker(lnglat, options {}) { const defaultIcon new T.Icon({ iconUrl: https://api.tianditu.gov.cn/v4.0/images/marker.png, iconSize: new T.Point(25, 34) }); const marker new T.Marker(lnglat, { icon: options.icon || defaultIcon, draggable: options.draggable || false }); map.addOverLay(marker); markers.push(marker); // 添加拖拽事件 if (options.draggable) { marker.addEventListener(dragend, function(e) { console.log(标记点移动到:, e.lnglat.lng, e.lnglat.lat); }); } return marker; } // 示例在点击位置添加标记点 map.addEventListener(click, function(e) { addMarker(e.lnglat, { draggable: true }); });对于批量添加标记点我们可以使用以下方法// 批量添加标记点 function addMarkers(coordinates) { coordinates.forEach(coord { addMarker(new T.LngLat(coord[0], coord[1])); }); } // 示例数据 const samplePoints [ [116.404, 39.915], [116.414, 39.925], [116.424, 39.935] ]; addMarkers(samplePoints);3. 绘制与编辑线要素线要素在地理信息系统中常用于表示道路、河流等线性特征。天地图提供了T.Polyline类来绘制折线并支持编辑功能。// 存储所有折线 let polylines []; // 绘制折线 function drawPolyline(points, options {}) { const lnglats points.map(p new T.LngLat(p[0], p[1])); const polyline new T.Polyline(lnglats, { color: options.color || #3388ff, weight: options.weight || 3, opacity: options.opacity || 1.0 }); map.addOverLay(polyline); polylines.push(polyline); return polyline; } // 示例绘制一条折线 const sampleLine [ [116.404, 39.915], [116.414, 39.925], [116.424, 39.935] ]; const myPolyline drawPolyline(sampleLine, { color: red });要实现交互式绘制我们可以使用天地图提供的T.PolylineToollet polylineTool; // 开始绘制折线 function startDrawingLine() { if (polylineTool) polylineTool.close(); polylineTool new T.PolylineTool(map, { color: #3388ff, weight: 3 }); polylineTool.open(); polylineTool.addEventListener(draw, function(e) { const points e.currentLnglats.map(ll [ll.lng, ll.lat]); drawPolyline(points); polylineTool.clear(); }); }编辑功能同样简单// 启用/禁用折线编辑 function togglePolylineEdit(polyline, enable) { if (enable) { polyline.enableEdit(); polyline.addEventListener(edit, function(e) { console.log(折线已编辑); }); } else { polyline.disableEdit(); } }4. 多边形绘制与高级编辑多边形是GIS中的重要要素用于表示区域、地块等。天地图的T.Polygon类提供了多边形绘制功能并支持顶点编辑。// 存储所有多边形 let polygons []; // 绘制多边形 function drawPolygon(points, options {}) { const lnglats points.map(p new T.LngLat(p[0], p[1])); const polygon new T.Polygon([lnglats], { // 注意points需要放在数组中 color: options.color || #3388ff, weight: options.weight || 3, opacity: options.opacity || 0.5, fillColor: options.fillColor || #3388ff, fillOpacity: options.fillOpacity || 0.2 }); map.addOverLay(polygon); polygons.push(polygon); return polygon; } // 示例绘制一个多边形 const samplePolygon [ [116.404, 39.915], [116.414, 39.925], [116.424, 39.915], [116.414, 39.905] ]; const myPolygon drawPolygon(samplePolygon, { color: green, fillColor: green });交互式绘制多边形let polygonTool; // 开始绘制多边形 function startDrawingPolygon() { if (polygonTool) polygonTool.close(); polygonTool new T.PolygonTool(map, { color: #3388ff, weight: 3, fillColor: #3388ff, fillOpacity: 0.2 }); polygonTool.open(); polygonTool.addEventListener(draw, function(e) { const points e.currentLnglats.map(ll [ll.lng, ll.lat]); drawPolygon(points); polygonTool.clear(); }); }多边形编辑功能// 启用/禁用多边形编辑 function togglePolygonEdit(polygon, enable) { if (enable) { polygon.enableEdit(); polygon.addEventListener(edit, function(e) { console.log(多边形已编辑); // 获取编辑后的顶点坐标 const vertices polygon.getLngLats()[0].map(ll [ll.lng, ll.lat]); console.log(当前顶点:, vertices); }); } else { polygon.disableEdit(); } }5. 综合应用与性能优化在实际项目中我们往往需要同时管理多种要素并提供统一的编辑界面。下面是一个综合管理的实现方案。// 要素管理器 const featureManager { points: [], lines: [], polygons: [], addPoint: function(lnglat) { const marker addMarker(lnglat, { draggable: true }); this.points.push({ id: Date.now(), marker: marker, coordinates: [lnglat.lng, lnglat.lat] }); }, addLine: function(points) { const polyline drawPolyline(points); this.lines.push({ id: Date.now(), polyline: polyline, coordinates: points }); }, addPolygon: function(points) { const polygon drawPolygon(points); this.polygons.push({ id: Date.now(), polygon: polygon, coordinates: points }); }, enableEditing: function() { this.points.forEach(item { item.marker.enableDragging(); item.marker.addEventListener(dragend, updatePointPosition); }); this.lines.forEach(item { item.polyline.enableEdit(); item.polyline.addEventListener(edit, updateLinePosition); }); this.polygons.forEach(item { item.polygon.enableEdit(); item.polygon.addEventListener(edit, updatePolygonPosition); }); }, disableEditing: function() { this.points.forEach(item { item.marker.disableDragging(); item.marker.removeEventListener(dragend, updatePointPosition); }); this.lines.forEach(item { item.polyline.disableEdit(); item.polyline.removeEventListener(edit, updateLinePosition); }); this.polygons.forEach(item { item.polygon.disableEdit(); item.polygon.removeEventListener(edit, updatePolygonPosition); }); }, clearAll: function() { this.points.forEach(item map.removeOverLay(item.marker)); this.lines.forEach(item map.removeOverLay(item.polyline)); this.polygons.forEach(item map.removeOverLay(item.polygon)); this.points []; this.lines []; this.polygons []; } }; // 更新点位置 function updatePointPosition(e) { const pointId /* 根据实际情况获取ID */; const point featureManager.points.find(p p.id pointId); if (point) { point.coordinates [e.lnglat.lng, e.lnglat.lat]; } } // 更新线位置 function updateLinePosition(e) { const lineId /* 根据实际情况获取ID */; const line featureManager.lines.find(l l.id lineId); if (line) { line.coordinates line.polyline.getLngLats().map(ll [ll.lng, ll.lat]); } } // 更新多边形位置 function updatePolygonPosition(e) { const polygonId /* 根据实际情况获取ID */; const polygon featureManager.polygons.find(p p.id polygonId); if (polygon) { polygon.coordinates polygon.polygon.getLngLats()[0].map(ll [ll.lng, ll.lat]); } }对于大量要素的场景性能优化尤为重要使用矢量瓦片替代单个要素对不必要的事件监听进行清理实现要素的按需加载使用Web Worker处理复杂计算// 性能优化示例批量操作使用setTimeout分片 function batchAddMarkers(coordinates, batchSize 100) { let index 0; function processBatch() { const batch coordinates.slice(index, index batchSize); batch.forEach(coord { addMarker(new T.LngLat(coord[0], coord[1])); }); index batchSize; if (index coordinates.length) { setTimeout(processBatch, 0); } } processBatch(); }6. 数据导出与持久化完成标注和编辑后我们通常需要将数据保存到服务器或导出为文件。以下是一些常见的数据处理方式。// 获取所有要素的GeoJSON格式数据 function getGeoJSONData() { const featureCollection { type: FeatureCollection, features: [] }; // 点要素 featureManager.points.forEach(item { featureCollection.features.push({ type: Feature, geometry: { type: Point, coordinates: item.coordinates }, properties: { id: item.id, type: point } }); }); // 线要素 featureManager.lines.forEach(item { featureCollection.features.push({ type: Feature, geometry: { type: LineString, coordinates: item.coordinates }, properties: { id: item.id, type: line } }); }); // 面要素 featureManager.polygons.forEach(item { featureCollection.features.push({ type: Feature, geometry: { type: Polygon, coordinates: [item.coordinates] // GeoJSON中多边形坐标是三维数组 }, properties: { id: item.id, type: polygon } }); }); return featureCollection; } // 导出为JSON文件 function exportToJSON() { const data getGeoJSONData(); const blob new Blob([JSON.stringify(data)], { type: application/json }); const url URL.createObjectURL(blob); const a document.createElement(a); a.href url; a.download map-features.json; a.click(); setTimeout(() URL.revokeObjectURL(url), 100); } // 保存到服务器 async function saveToServer() { const data getGeoJSONData(); try { const response await fetch(/api/save-features, { method: POST, headers: { Content-Type: application/json }, body: JSON.stringify(data) }); if (!response.ok) throw new Error(保存失败); console.log(数据保存成功); } catch (error) { console.error(保存出错:, error); } }从GeoJSON加载数据// 从GeoJSON加载要素 function loadFromGeoJSON(geojson) { if (!geojson || !geojson.features) return; geojson.features.forEach(feature { switch (feature.geometry.type) { case Point: featureManager.addPoint(new T.LngLat( feature.geometry.coordinates[0], feature.geometry.coordinates[1] )); break; case LineString: featureManager.addLine(feature.geometry.coordinates); break; case Polygon: // GeoJSON中多边形坐标是三维数组我们取第一个环 featureManager.addPolygon(feature.geometry.coordinates[0]); break; } }); } // 示例从文件加载 function handleFileUpload(event) { const file event.target.files[0]; if (!file) return; const reader new FileReader(); reader.onload function(e) { try { const geojson JSON.parse(e.target.result); featureManager.clearAll(); loadFromGeoJSON(geojson); } catch (error) { console.error(解析GeoJSON失败:, error); } }; reader.readAsText(file); }