在地图可视化中,散点图是最常见的需求之一。无论是展示位置信息、统计数据,还是标记兴趣点,都需要在地图上显示大量的点。今天就来学习 mapvthree 的 SimplePoint 组件,看看如何轻松实现点图层的可视化。
了解 SimplePoint
SimplePoint 是 mapvthree 提供的点图层组件,用于在地图上显示散点数据。
SimplePoint 的特点
- 简单易用:创建实例、设置数据源即可显示
- 样式丰富:支持颜色、大小等样式配置
- 数据驱动:支持根据数据动态设置颜色和大小
- 性能优秀:可以同时渲染上万个点
- 交互支持:支持点击、右键等事件
- 数据源灵活:支持 GeoJSON、JSON、CSV 等多种数据格式
我的理解:SimplePoint 就像是一个"点图层容器",我们只需要把数据放进去,它就会自动在地图上显示出来。
SimplePoint 与数据源的关系
SimplePoint 需要配合数据源(DataSource)使用:
SimplePoint(可视化组件)
↓ 绑定
DataSource(数据源)
↓ 包含
DataItem(数据项)
我的理解:
- SimplePoint 负责"怎么显示"(样式、渲染)
- DataSource 负责"显示什么"(数据内容)
- 两者配合,实现数据可视化
第一步:基本使用 - 显示简单的点
让我们从一个最简单的例子开始。
创建 SimplePoint 实例
js
import * as mapvthree from '@baidumap/mapv-three';
const container = document.getElementById('container');
const engine = new mapvthree.Engine(container, {
map: {
center: [116.404, 39.915],
range: 2000,
provider: null,
},
rendering: {
sky: null,
},
});
// 1. 创建 SimplePoint 实例
const point = engine.add(new mapvthree.SimplePoint({
size: 10, // 点的大小
}));
// 2. 准备数据(GeoJSON 格式)
const geoJSONData = {
type: 'FeatureCollection',
features: [
{
type: 'Feature',
geometry: {
type: 'Point',
coordinates: [116.404, 39.915, 0], // [经度, 纬度, 高度]
},
properties: {},
},
{
type: 'Feature',
geometry: {
type: 'Point',
coordinates: [116.414, 39.925, 0],
},
properties: {},
},
],
};
// 3. 创建数据源并绑定
const dataSource = mapvthree.GeoJSONDataSource.fromGeoJSON(geoJSONData);
point.dataSource = dataSource;
我的发现:只需要三步就能显示点图层:创建 SimplePoint → 准备数据 → 绑定数据源。
我的理解:
size参数控制点的大小(单位:像素)- 数据必须是 GeoJSON 格式的 Point 类型
- 坐标格式为
[经度, 纬度, 高度]
从 URL 加载数据
如果数据存储在服务器上,可以直接从 URL 加载:
js
// 创建 SimplePoint
const point = engine.add(new mapvthree.SimplePoint({
size: 15,
}));
// 从 URL 加载数据(异步)
const dataSource = await mapvthree.GeoJSONDataSource.fromURL('data/geojson/points.geojson');
point.dataSource = dataSource;
我的发现 :fromURL 是异步方法,需要使用 await 或 .then()。
我的理解:
fromURL会自动创建 GeoJSONDataSource 实例- 适合从服务器加载数据
- 需要确保数据格式是标准的 GeoJSON
第二步:设置点的颜色
SimplePoint 支持设置点的颜色,有两种方式。
方式 1:统一颜色
js
const point = engine.add(new mapvthree.SimplePoint({
size: 10,
color: 0xff0000, // 红色(十六进制)
}));
const dataSource = mapvthree.GeoJSONDataSource.fromGeoJSON(geoJSONData);
point.dataSource = dataSource;
我的理解:
color参数使用十六进制颜色值0xff0000= 红色,0x00ff00= 绿色,0x0000ff= 蓝色- 所有点都会显示相同的颜色
方式 2:基于数据的颜色(vertexColors)
如果想让不同的点显示不同的颜色,需要开启 vertexColors:
js
// 1. 开启 vertexColors
const point = engine.add(new mapvthree.SimplePoint({
size: 10,
vertexColors: true, // 开启基于数据的颜色
}));
// 2. 加载数据
const dataSource = mapvthree.GeoJSONDataSource.fromGeoJSON(geoJSONData);
// 3. 定义颜色属性
dataSource.defineAttribute('color', p => Math.random() * 0xffffff);
// 4. 绑定数据源
point.dataSource = dataSource;
我的发现:需要两步才能使用基于数据的颜色:
- 在 SimplePoint 中开启
vertexColors: true - 在数据源中定义
color属性
我的理解:
vertexColors: true告诉 SimplePoint:"我要使用数据中的颜色"defineAttribute('color', ...)告诉数据源:"如何从数据中获取颜色"- 两者配合,实现基于数据的颜色
使用回调函数定义颜色
defineAttribute 支持回调函数,可以根据数据动态计算颜色:
js
// 根据数据的 count 属性设置颜色
dataSource.defineAttribute('color', properties => {
const count = properties.count || 0;
if (count > 100) {
return 0xff0000; // 红色:数量大于 100
} else if (count > 50) {
return 0xffff00; // 黄色:数量大于 50
} else {
return 0x00ff00; // 绿色:数量小于等于 50
}
});
我的发现:回调函数可以灵活地根据数据计算颜色,实现更复杂的可视化效果。
使用数据中的颜色字段
如果数据中已经有颜色字段,可以直接使用:
js
// 数据格式
const geoJSONData = {
type: 'FeatureCollection',
features: [
{
type: 'Feature',
geometry: {
type: 'Point',
coordinates: [116.404, 39.915, 0],
},
properties: {
color: 0xff0000, // 数据中的颜色字段
},
},
],
};
// 直接使用数据中的 color 字段
dataSource.defineAttribute('color', 'color');
我的理解:
defineAttribute('color', 'color')表示使用数据中properties.color字段- 如果字段名不同,可以这样:
defineAttribute('color', 'myColor')
第三步:设置点的大小
SimplePoint 支持设置点的大小,也有两种方式。
方式 1:统一大小
js
const point = engine.add(new mapvthree.SimplePoint({
size: 15, // 所有点都是 15 像素大小
}));
方式 2:基于数据的大小(vertexSizes)
如果想让不同的点显示不同的大小,需要开启 vertexSizes:
js
// 1. 开启 vertexSizes
const point = engine.add(new mapvthree.SimplePoint({
vertexSizes: true, // 开启基于数据的大小
}));
// 2. 加载数据
const dataSource = mapvthree.GeoJSONDataSource.fromGeoJSON(geoJSONData);
// 3. 定义大小属性
dataSource.defineAttribute('size', p => Math.random() * 20 + 5); // 随机大小 5-25
// 4. 绑定数据源
point.dataSource = dataSource;
我的发现 :使用 vertexSizes 时,不需要在 SimplePoint 中设置 size 参数,大小完全由数据决定。
我的理解:
vertexSizes: true告诉 SimplePoint:"我要使用数据中的大小"defineAttribute('size', ...)告诉数据源:"如何从数据中获取大小"- 大小值单位是像素
同时使用颜色和大小
可以同时开启 vertexColors 和 vertexSizes:
js
const point = engine.add(new mapvthree.SimplePoint({
vertexColors: true, // 基于数据的颜色
vertexSizes: true, // 基于数据的大小
}));
const dataSource = mapvthree.GeoJSONDataSource.fromGeoJSON(geoJSONData);
// 定义颜色和大小属性
dataSource
.defineAttribute('color', p => Math.random() * 0xffffff)
.defineAttribute('size', p => Math.random() * 20 + 5);
point.dataSource = dataSource;
我的发现 :defineAttribute 支持链式调用,可以连续定义多个属性。
第四步:理解数据源系统
SimplePoint 必须配合数据源使用,理解数据源系统很重要。
数据源的类型
mapvthree 提供了多种数据源类型:
- GeoJSONDataSource:最常用,支持 GeoJSON 格式
- JSONDataSource:支持 JSON 格式,可以自定义解析
- CSVDataSource:支持 CSV 格式
- DataSource:基础数据源,可以手动添加数据
我的理解:GeoJSONDataSource 是最常用的,因为 GeoJSON 是地理数据的标准格式。
GeoJSON 数据格式
GeoJSON 是地理数据的标准格式,SimplePoint 需要 Point 类型的 GeoJSON:
js
const geoJSONData = {
type: 'FeatureCollection', // 必须
features: [ // 点数据数组
{
type: 'Feature', // 必须
geometry: {
type: 'Point', // 点类型
coordinates: [116.404, 39.915, 0], // [经度, 纬度, 高度]
},
properties: { // 可选,用于存储额外属性
name: '北京',
count: 100,
color: 0xff0000,
},
},
],
};
我的理解:
coordinates是点的位置,格式为[经度, 纬度, 高度]properties可以存储任意属性,用于定义颜色、大小等- 高度(z 值)是可选的,默认为 0
创建数据源
有两种方式创建 GeoJSONDataSource:
js
// 方式 1:从 GeoJSON 对象创建
const geoJSONData = { /* ... */ };
const dataSource = mapvthree.GeoJSONDataSource.fromGeoJSON(geoJSONData);
// 方式 2:从 URL 加载(异步)
const dataSource = await mapvthree.GeoJSONDataSource.fromURL('data/geojson/points.geojson');
我的发现 :fromGeoJSON 是同步的,fromURL 是异步的。
第五步:定义数据属性
数据属性是 SimplePoint 的核心功能,让我们深入理解一下。
什么是数据属性?
数据属性是将数据中的字段映射到可视化属性(颜色、大小等)的机制。
工作流程:
数据中的 properties
↓ defineAttribute 映射
可视化属性(color、size)
↓ vertexColors/vertexSizes 开启
SimplePoint 渲染
定义属性的两种方式
方式 1:直接使用字段名
js
// 数据格式
const geoJSONData = {
features: [{
properties: {
color: 0xff0000,
size: 20,
},
}],
};
// 直接使用字段名
dataSource.defineAttribute('color', 'color'); // 使用 properties.color
dataSource.defineAttribute('size', 'size'); // 使用 properties.size
方式 2:使用回调函数
js
// 使用回调函数动态计算
dataSource.defineAttribute('color', properties => {
// 根据 properties 计算颜色
return properties.count > 100 ? 0xff0000 : 0x00ff00;
});
dataSource.defineAttribute('size', properties => {
// 根据 properties 计算大小
return properties.count * 2;
});
我的理解:
- 回调函数的参数是
properties对象 - 返回值应该是颜色值(十六进制)或大小值(数字)
- 回调函数可以访问数据中的所有属性
链式调用
defineAttribute 支持链式调用,可以连续定义多个属性:
js
dataSource
.defineAttribute('color', p => Math.random() * 0xffffff)
.defineAttribute('size', p => Math.random() * 20 + 5)
.defineAttribute('opacity', p => p.count / 100); // 假设还有其他属性
我的发现:链式调用让代码更简洁,可读性更好。
第六步:事件交互
SimplePoint 支持多种事件,可以实现交互功能。
点击事件
js
const point = engine.add(new mapvthree.SimplePoint({
size: 10,
}));
point.dataSource = dataSource;
// 监听点击事件
point.addEventListener('click', e => {
console.log('点击了点:', e);
console.log('点的数据:', e.entity.value);
console.log('点的位置:', e.point);
});
我的发现 :事件对象 e 包含:
e.entity.value:点的数据(包含 properties)e.point:点的位置坐标
右键事件
js
// 监听右键事件
point.addEventListener('rightclick', e => {
console.log('右键点击了点:', e.entity.value);
});
动态修改数据
结合事件,可以实现动态修改数据:
js
// 点击点,改变颜色
point.addEventListener('click', e => {
const itemId = e.entity.value.id;
// 修改该点的颜色属性
dataSource.setAttributeValues(itemId, {
color: Math.random() * 0xffffff,
});
});
我的理解:
e.entity.value.id是数据项的唯一 IDsetAttributeValues可以更新数据项的属性值- 更新后,SimplePoint 会自动重新渲染
添加和删除数据
js
// 在地图空白处点击,添加新点
engine.map.addEventListener('click', e => {
if (!e.target) { // 没有点击到任何对象
// 创建新的数据项
const newItem = new mapvthree.DataItem([e.point[0], e.point[1], 0], {
color: Math.random() * 0xffffff,
size: 10,
});
// 添加到数据源
dataSource.add(newItem);
}
});
// 右键点击点,删除
point.addEventListener('rightclick', e => {
dataSource.remove(e.entity.value.id);
});
我的发现:可以动态添加和删除数据,SimplePoint 会自动更新显示。
第七步:性能优化
SimplePoint 的性能很好,但也有一些优化技巧。
大量点的渲染
SimplePoint 可以同时渲染上万个点:
js
// 生成 10000 个随机点
function generateRandomPoints(center, radius, count) {
const features = [];
for (let i = 0; i < count; i++) {
features.push({
type: 'Feature',
geometry: {
type: 'Point',
coordinates: [
center[0] + radius * (Math.random() - 0.5) * 2,
center[1] + radius * (Math.random() - 0.5) * 2,
0,
],
},
properties: {
count: Math.random() * 100,
},
});
}
return { type: 'FeatureCollection', features };
}
const point = engine.add(new mapvthree.SimplePoint({
size: 5,
vertexColors: true,
vertexSizes: true,
}));
const dataSource = mapvthree.GeoJSONDataSource.fromGeoJSON(
generateRandomPoints([116.404, 39.915], 0.02, 10000)
);
dataSource
.defineAttribute('color', p => Math.random() * 0xffffff)
.defineAttribute('size', p => Math.random() * 10);
point.dataSource = dataSource;
我的发现:即使有 10000 个点,性能依然很好,渲染流畅。
优化建议
- 合理设置点的大小:不要设置过大的点,会影响性能
- 使用数据属性 :开启
vertexColors和vertexSizes不会显著影响性能 - 避免频繁更新 :如果需要更新大量数据,使用
setData而不是逐个add/remove
js
// 不推荐:逐个添加
points.forEach(p => {
dataSource.add(new mapvthree.DataItem(p));
});
// 推荐:批量设置
const geoJSONData = { type: 'FeatureCollection', features: points };
dataSource.setData(geoJSONData);
第八步:完整示例
我想写一个完整的示例,把学到的都用上:
js
import * as mapvthree from '@baidumap/mapv-three';
const container = document.getElementById('container');
// 初始化引擎
const engine = new mapvthree.Engine(container, {
map: {
center: [116.404, 39.915],
range: 2000,
pitch: 60,
provider: null,
},
rendering: {
sky: null,
enableAnimationLoop: true,
},
});
// 生成随机点数据
function generatePoints(center, radius, count) {
const features = [];
for (let i = 0; i < count; i++) {
features.push({
type: 'Feature',
geometry: {
type: 'Point',
coordinates: [
center[0] + radius * (Math.random() - 0.5) * 2,
center[1] + radius * (Math.random() - 0.5) * 2,
Math.random() * 100, // 随机高度
],
},
properties: {
count: Math.random() * 100,
category: Math.floor(Math.random() * 3), // 0, 1, 2
},
});
}
return { type: 'FeatureCollection', features };
}
// 创建 SimplePoint
const point = engine.add(new mapvthree.SimplePoint({
vertexColors: true, // 基于数据的颜色
vertexSizes: true, // 基于数据的大小
}));
// 加载数据
const dataSource = mapvthree.GeoJSONDataSource.fromGeoJSON(
generatePoints([116.404, 39.915], 0.02, 1000)
);
// 定义属性:根据 category 设置颜色,根据 count 设置大小
dataSource
.defineAttribute('color', properties => {
const category = properties.category;
if (category === 0) return 0xff0000; // 红色
if (category === 1) return 0x00ff00; // 绿色
return 0x0000ff; // 蓝色
})
.defineAttribute('size', properties => {
return properties.count / 10 + 5; // 根据 count 计算大小
});
point.dataSource = dataSource;
// 添加点击事件
point.addEventListener('click', e => {
const data = e.entity.value;
console.log('点击了点:', {
count: data.properties.count,
category: data.properties.category,
position: e.point,
});
// 点击后改变颜色
dataSource.setAttributeValues(data.id, {
color: Math.random() * 0xffffff,
});
});
// 在地图空白处点击,添加新点
engine.map.addEventListener('click', e => {
if (!e.target) {
const newItem = new mapvthree.DataItem([e.point[0], e.point[1], 0], {
count: Math.random() * 100,
category: Math.floor(Math.random() * 3),
});
dataSource.add(newItem);
}
});
我的感受:掌握了 SimplePoint,就可以轻松实现各种点图层的可视化效果了!
第九步:踩过的坑
作为一个初学者,我踩了不少坑,记录下来避免再犯:
坑 1:数据不显示
原因:可能的原因有多个:
- 数据格式错误
- 坐标超出视野范围
- 数据源没有正确绑定
解决:
js
// 1. 检查数据格式
console.log('数据格式:', geoJSONData);
// 2. 检查坐标
console.log('坐标:', geoJSONData.features[0].geometry.coordinates);
// 3. 检查数据源绑定
console.log('数据源:', point.dataSource);
console.log('数据项数量:', point.dataSource.length);
坑 2:颜色不生效
原因 :忘记开启 vertexColors,或者没有定义 color 属性。
错误代码:
js
// 错误:只定义了属性,没有开启 vertexColors
const point = engine.add(new mapvthree.SimplePoint({
size: 10, // 没有 vertexColors: true
}));
dataSource.defineAttribute('color', 'color');
解决:
js
// 正确:开启 vertexColors
const point = engine.add(new mapvthree.SimplePoint({
vertexColors: true, // 必须开启
}));
dataSource.defineAttribute('color', 'color');
坑 3:大小不生效
原因 :忘记开启 vertexSizes,或者同时设置了 size 参数。
错误代码:
js
// 错误:同时设置了 size 和 vertexSizes
const point = engine.add(new mapvthree.SimplePoint({
size: 10, // 固定大小
vertexSizes: true, // 基于数据的大小
}));
// 固定大小会覆盖基于数据的大小
解决:
js
// 正确:只开启 vertexSizes
const point = engine.add(new mapvthree.SimplePoint({
vertexSizes: true, // 不要设置 size
}));
坑 4:从 URL 加载数据失败
原因 :fromURL 是异步方法,没有使用 await 或 .then()。
错误代码:
js
// 错误:没有等待异步加载
const dataSource = mapvthree.GeoJSONDataSource.fromURL('data/geojson/points.geojson');
point.dataSource = dataSource; // dataSource 可能还是 undefined
解决:
js
// 正确:使用 await
const dataSource = await mapvthree.GeoJSONDataSource.fromURL('data/geojson/points.geojson');
point.dataSource = dataSource;
// 或使用 .then()
mapvthree.GeoJSONDataSource.fromURL('data/geojson/points.geojson')
.then(dataSource => {
point.dataSource = dataSource;
});
坑 5:坐标格式错误
原因 :GeoJSON 的坐标格式是 [经度, 纬度, 高度],不是 [纬度, 经度, 高度]。
错误代码:
js
// 错误:纬度在前
coordinates: [39.915, 116.404, 0] // 错误!
解决:
js
// 正确:经度在前
coordinates: [116.404, 39.915, 0] // 正确
坑 6:defineAttribute 回调函数返回值错误
原因:颜色值必须是十六进制数字,不是字符串。
错误代码:
js
// 错误:返回字符串
dataSource.defineAttribute('color', p => '#ff0000'); // 错误!
解决:
js
// 正确:返回十六进制数字
dataSource.defineAttribute('color', p => 0xff0000); // 正确
坑 7:事件不触发
原因:可能没有正确绑定事件,或者事件被其他对象拦截。
解决:
js
// 确保在数据源绑定后添加事件
point.dataSource = dataSource;
// 然后添加事件监听
point.addEventListener('click', e => {
console.log('点击事件触发');
});
我的学习总结
经过这一天的学习,我掌握了:
- SimplePoint 的基本使用:创建实例、设置数据源即可显示
- 数据源系统:理解 GeoJSONDataSource 的使用方法
- 样式配置:统一颜色/大小 vs 基于数据的颜色/大小
- 数据属性 :通过
defineAttribute定义颜色和大小属性 - 事件交互:点击、右键等事件的使用
- 动态修改:添加、删除、修改数据的方法
- 性能优化:大量点的渲染技巧
我的感受:SimplePoint 用起来真的很简单!关键是理解数据源和可视化组件的关系,然后根据数据格式选择合适的加载方式。一旦掌握了基本用法,就可以轻松实现各种点图层的可视化效果。
关键要点:
- ✅ SimplePoint 必须配合数据源使用
- ✅ 使用基于数据的颜色/大小需要两步:开启标志 + 定义属性
- ✅ GeoJSON 坐标格式:
[经度, 纬度, 高度] - ✅
fromURL是异步方法,需要使用await - ✅ 颜色值必须是十六进制数字,不是字符串
下一步计划:
- 学习其他可视化图层(SimpleLine、Polygon 等)
- 尝试更复杂的数据可视化场景
- 学习如何结合多个图层实现复杂的地图效果
学习笔记就到这里啦!作为一个初学者,我觉得 SimplePoint 用起来真的很简单!关键是理解数据源和可视化组件的关系,然后根据数据格式选择合适的加载方式。希望我的笔记能帮到其他初学者!大家一起加油!