想要效果
问题
没有这一级别的地图geojson数据
DataV.GeoAtlas地理小工具系列
网站上获取地图信息,到黄岩区这一级后,就没有下一级别了,但是实际的业务中又需要划分出区域出来
思路
1.手动划线,设置区域
2.将划线生成坐标点
3.按区域生成多个区域数据
第一步
显示黄岩地图
第二步 设置划线逻辑
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>地图画线工具</title>
<script src="https://cdn.jsdelivr.net/npm/echarts@5.4.3/dist/echarts.min.js"></script>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
display: flex;
height: 100vh;
font-family: Arial, sans-serif;
}
#sidebar {
width: 350px;
background: #263238;
color: white;
padding: 20px;
overflow-y: auto;
}
#map { flex: 1; background: #0d47a1; }
h2 { margin-bottom: 20px; color: #4fc3f7; }
.btn {
width: 100%;
padding: 12px;
margin: 8px 0;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
}
.btn-red { background: #f44336; color: white; }
.btn-green { background: #4caf50; color: white; }
.btn-blue { background: #2196f3; color: white; }
.btn-orange { background: #ff9800; color: white; }
#points {
max-height: 200px;
overflow-y: auto;
background: #1a1a1a;
padding: 10px;
margin: 10px 0;
font-family: monospace;
font-size: 12px;
border-radius: 4px;
}
.point-row {
padding: 5px;
border-bottom: 1px solid #333;
display: flex;
justify-content: space-between;
}
textarea {
width: 100%;
height: 200px;
margin-top: 10px;
padding: 10px;
background: #1a1a1a;
color: #4caf50;
border: 1px solid #455a64;
font-family: monospace;
font-size: 11px;
resize: none;
}
.coord {
position: fixed;
bottom: 20px;
right: 20px;
background: rgba(0,0,0,0.9);
color: #4fc3f7;
padding: 15px 25px;
border-radius: 8px;
font-family: monospace;
font-size: 14px;
z-index: 1000;
}
.info {
background: rgba(76, 175, 80, 0.2);
padding: 10px;
border-left: 4px solid #4caf50;
margin: 10px 0;
font-size: 13px;
}
</style>
</head>
<body>
<div id="sidebar">
<h2>🗺️ 地图画线工具</h2>
<div class="info">
<strong>操作说明:</strong><br>
• 按住鼠标拖动:画线<br>
• 松开鼠标:结束画线<br>
• 画完后点击"生成坐标"
</div>
<button class="btn btn-orange" onclick="undo()">↩️ 撤销</button>
<button class="btn btn-red" onclick="clearAll()">🗑️ 清空</button>
<div style="margin: 15px 0;">
<strong>已采集点位:</strong>
<span id="count">0</span> 个
</div>
<button class="btn btn-green" onclick="generate()">📋 生成 Polygon 坐标</button>
<button class="btn btn-blue" onclick="copy()">📄 复制到剪贴板</button>
<textarea id="output" placeholder="点击"生成坐标"后显示..."></textarea>
</div>
<div id="map"></div>
<div class="coord" id="coord">经度: --, 纬度: --</div>
<script>
let points = [];
let chart = null;
let isDrawing = false;
let drawMode = false;
let tempLine = [];
// 加载地图
fetch('./黄岩区.geojson')
.then(r => r.json())
.then(data => {
echarts.registerMap('huangyan', data);
init();
})
.catch(e => alert('加载地图失败: ' + e.message));
function init() {
chart = echarts.init(document.getElementById('map'));
chart.setOption({
geo: {
map: 'huangyan',
roam: true,
layoutCenter: ['50%', '50%'],
layoutSize: '95%',
silent: true,
itemStyle: {
areaColor: 'rgba(25, 118, 210, 0.5)',
borderColor: '#4fc3f7',
borderWidth: 2
},
emphasis: {
disabled: false,
itemStyle: {
areaColor: 'rgba(79, 195, 247, 0.7)',
borderColor: '#fff',
borderWidth: 3
}
}
},
series: [
{
type: 'scatter',
coordinateSystem: 'geo',
data: [],
symbolSize: 15,
itemStyle: { color: '#ff5722', borderColor: '#fff', borderWidth: 2 },
label: { show: true, formatter: '{@index}', color: '#fff' },
zlevel: 10
},
{
type: 'lines',
coordinateSystem: 'geo',
polyline: true,
data: [],
lineStyle: { color: '#ff5722', width: 3 },
zlevel: 9
}
]
});
// 获取地理坐标的辅助函数
function getGeoCoord(e) {
// 使用 convertFromPixel 并指定 geo 坐标系
const coord = chart.convertFromPixel({ seriesIndex: 0 }, [e.offsetX, e.offsetY]);
if (!coord || isNaN(coord[0]) || isNaN(coord[1])) return null;
// 验证坐标在黄岩区范围内(大致范围)
if (coord[0] < 120.5 || coord[0] > 121.5 || coord[1] < 28.2 || coord[1] > 28.9) {
return null;
}
return coord;
}
// 鼠标坐标
chart.getZr().on('mousemove', e => {
const c = getGeoCoord(e);
if (c) document.getElementById('coord').textContent =
`经度: ${c[0].toFixed(6)}, 纬度: ${c[1].toFixed(6)}`;
});
// 当前正在画的轨迹点
let drawingPoints = [];
let lastDrawPoint = null;
// 鼠标按下,开始画线
chart.getZr().on('mousedown', e => {
isDrawing = true;
const coord = getGeoCoord(e);
if (coord) {
drawingPoints = [coord]; // 开始新轨迹
lastDrawPoint = coord;
drawLine();
}
});
// 鼠标移动,实时记录轨迹(间隔采集)
chart.getZr().on('mousemove', e => {
const c = getGeoCoord(e);
if (c) {
document.getElementById('coord').textContent =
`经度: ${c[0].toFixed(6)}, 纬度: ${c[1].toFixed(6)}`;
if (isDrawing && lastDrawPoint) {
// 计算与上一个点的距离
const dist = Math.sqrt(
Math.pow(c[0] - lastDrawPoint[0], 2) +
Math.pow(c[1] - lastDrawPoint[1], 2)
);
// 距离超过0.005度(约500米)才采集
if (dist > 0.005) {
drawingPoints.push(c);
lastDrawPoint = c;
drawLine();
}
}
}
});
// 鼠标松开,完成画线
chart.getZr().on('mouseup', () => {
if (!isDrawing) return;
isDrawing = false;
// 将轨迹点添加到总点集
if (drawingPoints.length > 0) {
points = points.concat(drawingPoints);
drawingPoints = [];
lastDrawPoint = null;
update();
}
});
// 实时画线
function drawLine() {
// 合并已有点和正在画的轨迹
const allPoints = [...points, ...drawingPoints];
if (chart) {
chart.setOption({
series: [
{
data: allPoints.map((p, i) => ({
value: p,
name: i < points.length ? i + 1 : '*'
}))
},
{
data: allPoints.length > 1 ? [{ coords: allPoints }] : []
}
]
});
}
}
// 禁用地图拖动,但允许缩放
chart.setOption({
geo: {
roam: true
}
});
// 快捷键:Ctrl+Z 撤销
document.addEventListener('keydown', (e) => {
if (e.ctrlKey && e.key === 'z') {
e.preventDefault();
undo();
}
});
window.addEventListener('resize', () => chart.resize());
}
// 只更新地图,不更新侧边栏(画线时使用,避免卡顿)
function updateMapOnly() {
document.getElementById('count').textContent = points.length;
if (chart) {
chart.setOption({
series: [
{ data: points.map((p, i) => ({ value: p, name: i + 1 })) },
{ data: points.length > 1 ? [{ coords: points }] : [] }
]
});
}
}
function update() {
// 只更新计数和地图
document.getElementById('count').textContent = points.length;
if (chart) {
chart.setOption({
series: [
{ data: points.map((p, i) => ({ value: p, name: i + 1 })) },
{ data: points.length > 1 ? [{ coords: points }] : [] }
]
});
}
}
function undo() {
points.pop();
update();
}
function clearAll() {
if (confirm('清空所有点?')) {
points = [];
update();
}
}
function closePolygon() {
if (points.length < 3) {
alert('至少需要3个点');
return;
}
// 如果首尾不相同,添加首点作为尾点
const first = points[0];
const last = points[points.length - 1];
if (first[0] !== last[0] || first[1] !== last[1]) {
points.push([...first]);
update();
}
alert('已闭合!');
}
function generate() {
if (points.length < 3) {
alert('至少需要3个点');
return;
}
// 生成格式化的坐标数组
const coords = points.map(p => ` [${p[0].toFixed(6)}, ${p[1].toFixed(6)}]`).join(',\n');
const output = `"coordinates": [
[
${coords}
]
]`;
document.getElementById('output').value = output;
}
function copy() {
const out = document.getElementById('output');
if (!out.value) return alert('请先生成坐标');
out.select();
document.execCommand('copy');
alert('已复制!粘贴到 GeoJSON 的 coordinates 字段');
}
// 初始化显示
update();
</script>
</body>
</html>
划线测试
生成坐标点
"coordinates": [
[
[120.993998, 28.689548],
[120.987912, 28.694942],
[120.975741, 28.701996],
[120.960804, 28.709465],
[120.953059, 28.721082],
[120.927057, 28.724402],
[120.920418, 28.716933],
[120.915439, 28.707805],
[120.907694, 28.703656],
[120.908247, 28.693283],
[120.906588, 28.679175],
[120.902162, 28.669632],
[120.901608, 28.660089],
[120.896629, 28.665068],
[120.889991, 28.666727],
[120.882799, 28.666727],
[120.875053, 28.662578],
[120.885011, 28.655939],
[120.887778, 28.649716],
[120.893863, 28.644737],
[120.905481, 28.636023],
[120.913780, 28.628140],
[120.913780, 28.625235],
[120.903821, 28.624405],
[120.880586, 28.627725],
[120.867861, 28.629799],
[120.863989, 28.624405],
[120.855690, 28.620671],
[120.828582, 28.617352],
[120.811985, 28.617352],
[120.805899, 28.611128],
[120.805346, 28.601170],
[120.798154, 28.594116],
[120.795388, 28.590382],
[120.796495, 28.579179],
[120.801474, 28.570465],
[120.814198, 28.567561],
[120.826369, 28.567146],
[120.826369, 28.558433],
[120.829135, 28.553453],
[120.841859, 28.545155],
[120.836880, 28.533122],
[120.833561, 28.527728],
[120.839093, 28.518185],
[120.849605, 28.515281],
[120.854031, 28.514451],
[120.857350, 28.505737],
[120.863989, 28.494949],
[120.875607, 28.490385],
[120.897736, 28.486236],
[120.912673, 28.494119],
[120.924291, 28.503248],
[120.948080, 28.500343],
[120.952506, 28.509057],
[120.961911, 28.509887],
[120.974635, 28.538516],
[120.971316, 28.558847],
[120.980167, 28.574200],
[120.996764, 28.578349],
[121.006722, 28.599925],
[121.005616, 28.616522],
[121.023319, 28.625235],
[121.036597, 28.621916],
[121.046002, 28.641417],
[121.034384, 28.665898],
[121.031618, 28.681665],
[121.012808, 28.680005],
[121.002850, 28.686644]
]
]
渲染
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<title>地图显示</title>
<script src="https://cdn.jsdelivr.net/npm/echarts@5.4.3/dist/echarts.min.js"></script>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { height: 100vh; background: #0d47a1; }
#map { width: 100%; height: 100%; }
.color-picker {
position: fixed; top: 20px; right: 20px;
background: rgba(0,0,0,0.8); color: white;
padding: 15px 20px; border-radius: 8px;
}
.color-picker input { width: 60px; height: 30px; border: none; }
</style>
</head>
<body>
<div id="map"></div>
<div class="color-picker">
<label>背景色:</label>
<input type="color" id="bgColor" value="#0d47a1" />
</div>
<script>
// 1. 定义颜色映射(和GeoJSON的name一一对应)
const colorMap = {
"宁溪所": "rgba(255, 87, 34, 0.6)",
"北洋所": "rgba(76, 175, 80, 0.6)",
"北新所": "rgba(33, 150, 243, 0.6)",
"东江所": "rgba(255, 152, 0, 0.6)",
"院桥所": "rgba(156, 39, 176, 0.6)",
"西澄所": "rgba(0, 188, 212, 0.6)",
"黄岩区": "rgba(25, 118, 210, 0.5)"
};
// 2. 加载GeoJSON并初始化地图
fetch('./黄岩区.geojson')
.then(r => {
// 验证GeoJSON加载成功
if (!r.ok) throw new Error('GeoJSON文件加载失败');
return r.json();
})
.then(geoJson => {
// 打印GeoJSON中的所有name,方便你核对
console.log('GeoJSON中的区域名称:');
geoJson.features.forEach(feature => {
console.log('- ' + feature.properties.name);
});
// 注册地图
echarts.registerMap('huangyan', geoJson);
const chart = echarts.init(document.getElementById('map'));
// 3. 构造地图数据(确保每个区域都有对应数据,并设置颜色)
const mapData = Object.keys(colorMap).map(name => ({
name: name,
value: Math.random(), // 随便给个值
itemStyle: {
areaColor: colorMap[name] // 直接为每个区域设置颜色
}
}));
// 4. 配置项(极简版,只保留核心)
const option = {
backgroundColor: '#0d47a1',
series: [{
type: 'map',
map: 'huangyan',
roam: true, // 支持缩放/拖拽
data: mapData,
// 区域样式
itemStyle: {
borderColor: '#4fc3f7',
borderWidth: 2
},
// 高亮样式
emphasis: {
itemStyle: {
borderColor: '#fff',
borderWidth: 3,
areaColor: function(params) {
// 高亮时颜色加深
const color = colorMap[params.name];
if (color) {
return color.replace('0.6)', '0.9)').replace('0.5)', '0.8)');
}
return '#ff9800';
}
}
},
// 文字标签
label: {
show: true,
color: '#fff',
fontSize: 14,
fontWeight: 'bold'
}
}]
};
// 渲染地图
chart.setOption(option);
window.addEventListener('resize', () => chart.resize());
// 背景色切换
document.getElementById('bgColor').addEventListener('input', (e) => {
chart.setOption({ backgroundColor: e.target.value });
document.body.style.background = e.target.value;
});
})
.catch(e => {
alert('错误:' + e.message);
console.error('详细错误:', e);
});
</script>
</body>
</html>
多次绘制实现最终效果