JSAPIThree 加载简单点图层学习笔记:SimplePoint 散点可视化

在地图可视化中,散点图是最常见的需求之一。无论是展示位置信息、统计数据,还是标记兴趣点,都需要在地图上显示大量的点。今天就来学习 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;

我的发现:需要两步才能使用基于数据的颜色:

  1. 在 SimplePoint 中开启 vertexColors: true
  2. 在数据源中定义 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', ...) 告诉数据源:"如何从数据中获取大小"
  • 大小值单位是像素

同时使用颜色和大小

可以同时开启 vertexColorsvertexSizes

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 是数据项的唯一 ID
  • setAttributeValues 可以更新数据项的属性值
  • 更新后,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 个点,性能依然很好,渲染流畅。

优化建议

  1. 合理设置点的大小:不要设置过大的点,会影响性能
  2. 使用数据属性 :开启 vertexColorsvertexSizes 不会显著影响性能
  3. 避免频繁更新 :如果需要更新大量数据,使用 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:数据不显示

原因:可能的原因有多个:

  1. 数据格式错误
  2. 坐标超出视野范围
  3. 数据源没有正确绑定

解决

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('点击事件触发');
});

我的学习总结

经过这一天的学习,我掌握了:

  1. SimplePoint 的基本使用:创建实例、设置数据源即可显示
  2. 数据源系统:理解 GeoJSONDataSource 的使用方法
  3. 样式配置:统一颜色/大小 vs 基于数据的颜色/大小
  4. 数据属性 :通过 defineAttribute 定义颜色和大小属性
  5. 事件交互:点击、右键等事件的使用
  6. 动态修改:添加、删除、修改数据的方法
  7. 性能优化:大量点的渲染技巧

我的感受:SimplePoint 用起来真的很简单!关键是理解数据源和可视化组件的关系,然后根据数据格式选择合适的加载方式。一旦掌握了基本用法,就可以轻松实现各种点图层的可视化效果。

关键要点

  • ✅ SimplePoint 必须配合数据源使用
  • ✅ 使用基于数据的颜色/大小需要两步:开启标志 + 定义属性
  • ✅ GeoJSON 坐标格式:[经度, 纬度, 高度]
  • fromURL 是异步方法,需要使用 await
  • ✅ 颜色值必须是十六进制数字,不是字符串

下一步计划

  1. 学习其他可视化图层(SimpleLine、Polygon 等)
  2. 尝试更复杂的数据可视化场景
  3. 学习如何结合多个图层实现复杂的地图效果

学习笔记就到这里啦!作为一个初学者,我觉得 SimplePoint 用起来真的很简单!关键是理解数据源和可视化组件的关系,然后根据数据格式选择合适的加载方式。希望我的笔记能帮到其他初学者!大家一起加油!

相关推荐
蓝田生玉1237 小时前
BEVFormer论文阅读笔记
论文阅读·笔记
西瓜堆7 小时前
提示词工程学习笔记: 工程技术行业提示词推荐
笔记·学习
wxr06169 小时前
GOF笔记
笔记·适配器·ooad
极海拾贝9 小时前
GeoScene解决方案中心正式上线!
大数据·人工智能·深度学习·arcgis·信息可视化·语言模型·解决方案
charlie11451419110 小时前
嵌入式的现代C++教程——constexpr与设计技巧
开发语言·c++·笔记·单片机·学习·算法·嵌入式
好奇龙猫10 小时前
【AI学习-comfyUI学习-三十二节-FLXU原生态反推+controlnet depth(UNion)工作流-各个部分学习】
人工智能·学习
童话名剑10 小时前
锚框 与 完整YOLO示例(吴恩达深度学习笔记)
笔记·深度学习·yolo··anchor box
好奇龙猫11 小时前
【大学院-筆記試験練習:数据库(データベース問題訓練) と 软件工程(ソフトウェア)(7)】
学习
j_jiajia12 小时前
(一)人工智能算法之监督学习——KNN
人工智能·学习·算法