Skip to content

feat: 新增 PointLayer shape: image 新增 allowOverlap 配置,用于控制图片之间自动避让隐藏 - #2828

Merged
heiyexing merged 2 commits into
masterfrom
feat/yanxiong-overlap
Mar 11, 2026
Merged

feat: 新增 PointLayer shape: image 新增 allowOverlap 配置,用于控制图片之间自动避让隐藏#2828
heiyexing merged 2 commits into
masterfrom
feat/yanxiong-overlap

Conversation

@heiyexing

Copy link
Copy Markdown
Contributor

[English Template / 英文模板]

🤔 这个变动的性质是?

  • 新特性提交
  • 日常 bug 修复
  • 站点、文档改进
  • 演示代码改进
  • 组件样式/交互改进
  • TypeScript 定义更新
  • 包体积优化
  • 性能优化
  • 功能增强
  • 国际化改进
  • 重构
  • 代码风格优化
  • 测试用例
  • 分支合并
  • 工作流程
  • 版本更新
  • 其他改动(是关于什么的改动?)

🔗 相关 Issue

💡 需求背景和解决方案

📝 更新日志

语言 更新描述
🇺🇸 英文
🇨🇳 中文

☑️ 请求合并前的自查清单

⚠️ 请自检并全部勾选全部选项⚠️

  • 文档已补充或无须补充
  • 代码演示已提供或无须提供
  • TypeScript 定义已补充或无须补充
  • Changelog 已提供或无须提供

@heiyexing
heiyexing requested a review from lzxue March 11, 2026 08:21
@changeset-bot

changeset-bot Bot commented Mar 11, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 68a73c9

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@gemini-code-assist

Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

此拉取请求的核心目标是增强 PointLayer 在显示图片图标时的用户体验,通过引入 allowOverlap 配置,解决了地图上大量图片图标相互重叠导致信息难以辨识的问题。现在,开发者可以灵活控制图标是否允许压盖,并在禁用压盖时,系统会自动进行碰撞检测并隐藏部分图标,确保地图界面的清晰和整洁。这一改进对于需要展示密集点位图标的场景尤为重要,显著提升了地图的可视化效果和信息传达效率。

Highlights

  • 新增 PointLayer 图片压盖控制: 为 PointLayer 的 shape: image 类型新增了 allowOverlap 配置项,当设置为 false 时,将启用碰撞检测,自动隐藏重叠的图片图标,以提高地图的可读性。
  • 新增 padding 配置: 在 allowOverlap: false 模式下,新增了 padding 配置项,允许用户为碰撞检测的包围盒设置额外的水平和垂直边距,以控制图标之间的间距。
  • 碰撞检测逻辑实现: 在 ImageModel 中实现了基于屏幕空间的 AABB 碰撞检测逻辑,并在地图缩放、平移或 allowOverlap 状态改变时触发重新计算和模型重建。
  • 文档与示例更新: 更新了英文和中文的 PointLayer 样式文档,详细说明了 allowOverlappadding 的用法,并新增了一个交互式示例 allowOverlap.js 来演示该功能。
Changelog
  • package.json
    • 新增了 @antv/async-hook 依赖。
  • packages/layers/src/core/interface.ts
    • IPointLayerStyleOptions 接口中新增了 allowOverlap 属性,用于控制图片类型图标的压盖行为。
  • packages/layers/src/core/triangulation.ts
    • 修改了 PointImageTriangulation 函数,使其能够根据 imageFilterMap 判断并跳过未通过碰撞检测的 feature,从而实现隐藏效果。
  • packages/layers/src/point/index.ts
    • 引入了 ILayerILayerConfig 类型。
    • 重写了 style 方法,当 allowOverlap 选项发生变化时,主动触发 reRender 以确保即时生效。
  • packages/layers/src/point/models/image.ts
    • 引入了 boundsContains, calculateCentroid, padBoundsCollisionIndex 等工具函数。
    • 新增了 imageFilterMap, currentZoom, extent, preAllowOverlap 等内部状态属性,用于管理碰撞检测。
    • 修改了 initModels 方法,初始化了 extentpreAllowOverlap 状态。
    • 修改了 buildModels 方法,根据 allowOverlap 的值决定是否调用 filterImages 进行碰撞检测,并绑定 thisPointImageTriangulation
    • 新增了 needUpdate 方法,用于在 allowOverlap 状态改变、地图缩放或视野范围变化时,触发模型重建。
    • 调整了 clearModels 方法的位置。
    • 新增了 filterImages 私有方法,实现了基于屏幕空间的 AABB 碰撞检测,并更新 imageFilterMap
    • 新增了 imageExtent 私有方法,用于计算带填充的地图边界。
    • 新增了 reBuildModel 私有方法,用于根据 allowOverlap 状态重建模型。
  • site/docs/api/point_layer/style.en.md
    • 更新了 PointLayer 样式文档,新增了 allowOverlappadding 属性的英文描述和示例。
  • site/docs/api/point_layer/style.zh.md
    • 更新了 PointLayer 样式文档,新增了 allowOverlappadding 属性的中文描述和示例。
  • site/examples/point/image/demo/allowOverlap.js
    • 新增了一个名为 allowOverlap.js 的示例文件,演示了 PointLayer 图片图标的压盖控制功能。
  • site/examples/point/image/demo/meta.json
    • 更新了 meta.json 文件,添加了 allowOverlap.js 示例的元数据。
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@heiyexing
heiyexing merged commit 6af1682 into master Mar 11, 2026
8 checks passed
@heiyexing
heiyexing deleted the feat/yanxiong-overlap branch March 11, 2026 08:30

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

本次 PR 新增了 PointLayershape: image 类型的 allowOverlap 配置,用于控制图片标注的碰撞避让,这是一个很有用的功能。

代码实现方面,核心逻辑主要在 packages/layers/src/point/models/image.ts 中,通过 CollisionIndex 在屏幕空间进行 AABB 碰撞检测,并在 PointImageTriangulation 中过滤掉被遮挡的图标,整体思路清晰。

我发现了一些可以改进的地方:

  1. image.ts 中,buildModelsreBuildModel 方法存在代码重复,可以提取一个私有方法来优化。
  2. 同样在 image.ts 中,对没有 id 的 feature 赋予默认值 0 可能会导致多个 feature 共享同一 id,从而在碰撞检测中出现问题。
  3. triangulation.ts 中,使用了 @ts-ignore 来处理 this 的类型,有更优雅的 TypeScript 写法。

具体的修改建议请见代码评论。除此之外,文档和示例的更新也很完整,做得很好。

const data = this.layer.getEncodedData();

data.forEach((feature: IEncodeFeature) => {
const { id = 0, size = 5 } = feature;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

此处为 id 设置默认值 0 可能会导致问题。如果多个 feature 的 id 都是 undefined,它们都会被赋予 id = 0。这会导致在 imageFilterMapcollisionIndex 中互相覆盖,使得只有一个 id0 的 feature 能够被正确处理,而其他没有 id 的 feature 会被错误地隐藏或显示。

建议修改此行,并在下一行添加对 id 是否为 undefined 的检查,如果是则跳过当前 feature:

if (id === undefined) {
  // console.warn('Feature without id is skipped in collision detection.');
  return;
}
Suggested change
const { id = 0, size = 5 } = feature;
const { id, size = 5 } = feature;

Comment on lines +135 to +136
// @ts-ignore
const that = this as { imageFilterMap?: { [id: number]: boolean } };

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

使用 @ts-ignorethat 变量来为 this 提供类型不是最佳实践。TypeScript 支持在函数的第一个参数位置声明 this 的类型,这样做可以更清晰地表达函数对 this 上下文的依赖,并且能获得更好的类型检查。

建议将函数签名修改为 export function PointImageTriangulation(this: { imageFilterMap?: { [id: number]: boolean } }, feature: IEncodeFeature),然后就可以在函数体内直接安全地使用 this,无需 that 变量和类型断言。

Comment on lines +223 to +241
private async reBuildModel() {
const { allowOverlap = true } = this.layer.getLayerConfig() as IPointLayerStyleOptions;
if (!allowOverlap) {
this.filterImages();
} else {
this.imageFilterMap = null; // 允许压盖:清空过滤表,全量渲染
}
const model = await this.layer.buildLayerModel({
moduleName: 'pointImage',
vertexShader: pointImageVert,
fragmentShader: pointImageFrag,
triangulation: PointImageTriangulation.bind(this),
defines: this.getDefines(),
inject: this.getInject(),
depth: { enable: false },
primitive: gl.POINTS,
});
this.layer.models = [model];
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

此方法 reBuildModelbuildModels 方法存在大量重复代码。为了提高代码的可维护性,建议提取一个私有的辅助方法来封装公共的模型创建逻辑,以减少重复并使代码更清晰。

例如,可以创建一个 _buildModel 方法:

private async _buildModel(): Promise<IModel> {
  const { allowOverlap = true } = this.layer.getLayerConfig() as IPointLayerStyleOptions;
  if (!allowOverlap) {
    this.filterImages();
  } else {
    this.imageFilterMap = null;
  }
  return await this.layer.buildLayerModel({
    moduleName: 'pointImage',
    vertexShader: pointImageVert,
    fragmentShader: pointImageFrag,
    triangulation: PointImageTriangulation.bind(this),
    defines: this.getDefines(),
    inject: this.getInject(),
    depth: { enable: false },
    primitive: gl.POINTS,
  });
}

然后 buildModelsreBuildModel 可以简化为:

public async buildModels(): Promise<IModel[]> {
  this.initUniformsBuffer();
  const model = await this._buildModel();
  return [model];
}

private async reBuildModel() {
  const model = await this._buildModel();
  this.layer.models = [model];
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants