fix(master): merge master branch fix conflict

This commit is contained in:
thinkinggis 2019-11-25 17:29:48 +08:00
commit ea3e6ca5d5
244 changed files with 5493 additions and 9331 deletions

View File

@ -1,400 +0,0 @@
.l7-marker-container {
width:100%
}
.l7-marker {
position: absolute !important;
top: 0;
left: 0;
z-index: 5;
}
.l7-popup-anchor-top,
.l7-popup-anchor-top-left,
.l7-popup-anchor-top-right {
-webkit-flex-direction: column;
flex-direction: column;
}
.l7-popup-anchor-bottom,
.l7-popup-anchor-bottom-left,
.l7-popup-anchor-bottom-right {
-webkit-flex-direction: column-reverse;
flex-direction: column-reverse;
}
.l7-popup-anchor-left {
-webkit-flex-direction: row;
flex-direction: row;
}
.l7-popup-anchor-right {
-webkit-flex-direction: row-reverse;
flex-direction: row-reverse;
}
.l7-popup {
position: absolute;
top: 0;
left: 0;
display: -webkit-flex;
display: flex;
will-change: transform;
pointer-events: none;
z-index: 5;
}
.l7-popup-tip {
width: 0;
height: 0;
border: 10px solid transparent;
z-index: 1;
}
.l7-popup-anchor-top .l7-popup-tip {
-webkit-align-self: center;
align-self: center;
border-top: none;
border-bottom-color: #fff;
}
.l7-popup-anchor-top-left .l7-popup-tip {
-webkit-align-self: flex-start;
align-self: flex-start;
border-top: none;
border-left: none;
border-bottom-color: #fff;
}
.l7-popup-anchor-top-right .l7-popup-tip {
-webkit-align-self: flex-end;
align-self: flex-end;
border-top: none;
border-right: none;
border-bottom-color: #fff;
}
.l7-popup-anchor-bottom .l7-popup-tip {
-webkit-align-self: center;
align-self: center;
border-bottom: none;
border-top-color: #fff;
}
.l7-popup-anchor-bottom-left .l7-popup-tip {
-webkit-align-self: flex-start;
align-self: flex-start;
border-bottom: none;
border-left: none;
border-top-color: #fff;
}
.l7-popup-anchor-bottom-right .l7-popup-tip {
-webkit-align-self: flex-end;
align-self: flex-end;
border-bottom: none;
border-right: none;
border-top-color: #fff;
}
.l7-popup-anchor-left .l7-popup-tip {
-webkit-align-self: center;
align-self: center;
border-left: none;
border-right-color: #fff;
}
.l7-popup-anchor-right .l7-popup-tip {
-webkit-align-self: center;
align-self: center;
border-right: none;
border-left-color: #fff;
}
.l7-popup-close-button {
position: absolute;
right: 0;
top: 0;
border: 0;
padding: 0;
font-size: 25px;
line-height: 20px;
border-radius: 0 3px 0 0;
cursor: pointer;
background-color: transparent;
}
.l7-popup-close-button:hover {
background-color: rgba(0, 0, 0, 0.05);
}
.l7-popup-content {
position: relative;
background: #fff;
border-radius: 3px;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
padding: 10px 10px 15px;
pointer-events: auto;
}
.l7-popup-anchor-top-left .l7-popup-content {
border-top-left-radius: 0;
}
.l7-popup-anchor-top-right .l7-popup-content {
border-top-right-radius: 0;
}
.l7-popup-anchor-bottom-left .l7-popup-content {
border-bottom-left-radius: 0;
}
.l7-popup-anchor-bottom-right .l7-popup-content {
border-bottom-right-radius: 0;
}
.l7-popup-track-pointer {
display: none;
}
.l7-popup-track-pointer * {
pointer-events: none;
user-select: none;
}
.l7-map:hover .l7-popup-track-pointer {
display: flex;
}
.l7-map:active .l7-popup-track-pointer {
display: none;
}
.l7-popup-close-button {
position: absolute;
right: 0;
top: 0;
border: 0;
border-radius: 0 3px 0 0;
cursor: pointer;
background-color: transparent;
}
.l7-popup-close-button:hover {
background-color: rgba(0, 0, 0, 0.05);
}
.l7-popup-content {
position: relative;
background: #fff;
border-radius: 3px;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
padding: 10px 10px 15px;
pointer-events: auto;
}
/* general toolbar styles */
.l7-bar {
box-shadow: 0 1px 5px rgba(0,0,0,0.65);
border-radius: 4px;
}
.l7-bar a,
.l7-bar a:hover {
background-color: #fff;
border-bottom: 1px solid #ccc;
width: 26px;
height: 26px;
line-height: 26px;
display: block;
text-align: center;
text-decoration: none;
color: black;
}
.l7-bar a,
.l7-control-layers-toggle {
background-position: 50% 50%;
background-repeat: no-repeat;
display: block;
}
.l7-bar a:hover {
background-color: #f4f4f4;
}
.l7-bar a:first-child {
border-top-left-radius: 4px;
border-top-right-radius: 4px;
}
.l7-bar a:last-child {
border-bottom-left-radius: 4px;
border-bottom-right-radius: 4px;
border-bottom: none;
}
.l7-bar a.l7-disabled {
cursor: default;
background-color: #f4f4f4;
color: #bbb;
}
/* control positioning */
.l7-control-container {
font: 12px/1.5 "Helvetica Neue", Arial, Helvetica, sans-serif;
}
.l7-control-hide {
display: none;
}
.l7-control {
position: relative;
z-index: 800;
pointer-events: visiblePainted; /* IE 9-10 doesn't have auto */
pointer-events: auto;
}
.l7-top,
.l7-bottom {
position: absolute;
z-index: 1000;
pointer-events: none;
}
.l7-top {
top: 0;
}
.l7-right {
right: 0;
}
.l7-bottom {
bottom: 0;
}
.l7-left {
left: 0;
}
.l7-control {
float: left;
clear: both;
}
.l7-right .l7-control {
float: right;
}
.l7-top .l7-control {
margin-top: 10px;
}
.l7-bottom .l7-control {
margin-bottom: 10px;
}
.l7-left .l7-control {
margin-left: 10px;
}
.l7-right .l7-control {
margin-right: 10px;
}
/* attribution and scale controls */
.l7-control-container .l7-control-attribution {
background: #fff;
background: rgba(255, 255, 255, 0.7);
margin: 0;
}
.l7-control-attribution,
.l7-control-scale-line {
padding: 0 5px;
color: #333;
}
.l7-control-attribution a {
text-decoration: none;
}
.l7-control-attribution a:hover {
text-decoration: underline;
}
.l7-container .l7-control-attribution,
.l7-container .l7-control-scale {
font-size: 11px;
}
.l7-left .l7-control-scale {
margin-left: 5px;
}
.l7-bottom .l7-control-scale {
margin-bottom: 5px;
}
.l7-control-scale-line {
border: 2px solid #777;
border-top: none;
line-height: 1.1;
padding: 2px 5px 1px;
font-size: 11px;
white-space: nowrap;
overflow: hidden;
-moz-box-sizing: border-box;
box-sizing: border-box;
background: #fff;
background: rgba(255, 255, 255, 0.5);
}
.l7-control-scale-line:not(:first-child) {
border-top: 2px solid #777;
border-bottom: none;
margin-top: -2px;
}
.l7-control-scale-line:not(:first-child):not(:last-child) {
border-bottom: 2px solid #777;
}
.l7-touch .l7-control-attribution,
.l7-touch .l7-control-layers,
.l7-touch .l7-bar {
box-shadow: none;
}
.l7-touch .l7-control-layers,
.l7-touch .l7-bar {
border: 2px solid rgba(0,0,0,0.2);
background-clip: padding-box;
}
/* layers control */
.l7-control-layers {
box-shadow: 0 1px 5px rgba(0,0,0,0.4);
background: #fff;
border-radius: 5px;
}
.l7-control-layers-toggle {
background-image: url(../images/layers.svg);
width: 36px;
height: 36px;
}
.l7-retina .l7-control-layers-toggle {
background-image: url(../images/layers.svg);
background-size: 26px 26px;
}
.l7-touch .l7-control-layers-toggle {
width: 44px;
height: 44px;
}
.l7-control-layers .l7-control-layers-list,
.l7-control-layers-expanded .l7-control-layers-toggle {
display: none;
}
.l7-control-layers-expanded .l7-control-layers-list {
display: block;
position: relative;
}
.l7-control-layers-expanded {
padding: 6px 10px 6px 6px;
color: #333;
background: #fff;
}
.l7-control-layers-scrollbar {
overflow-y: scroll;
overflow-x: hidden;
padding-right: 5px;
}
.l7-control-layers-selector {
margin-top: 2px;
position: relative;
top: 1px;
}
.l7-control-layers label {
display: block;
}
.l7-control-layers-separator {
height: 0;
border-top: 1px solid #ddd;
margin: 5px -10px 5px -6px;
}

View File

@ -26,41 +26,14 @@ yarn install
yarn watch
```
运行 Demo
运行 Demo,基于 Storybook
```bash
yarn storybook
```
代替 `git commit` 提交:
```bash
yarn commit
```
## view doc example
```bash
npm start
yarn start
```
visit http://localhost:8000/
## Add Package
add new package
```bash
lerna create my-pack -y
```
将 ui-lib 作为 my-pack 的依赖:
```bash
yarn workspace my-pack add ui-lib/1.0.0
```
将 lodash 添加为所有 package 的依赖(不包含root
```bash
yarn workspaces run add lodash
```
将 typescript 设置为 root 的开发依赖
```bash
yarn add -W -D typescript jest
```

View File

@ -9,7 +9,7 @@ L7 是由蚂蚁金服 AntV 数据可视化团队推出的基于 WebGL 的开源
### Installation
```
npm install @antv/l7
npm install @antv/l7@beta
```
## 核心特性

View File

@ -1,11 +1,14 @@
// @see https://babeljs.io/docs/en/next/config-files#project-wide-configuration
module.exports = api => {
api.cache(() => process.env.NODE_ENV);
const isSite = api.env('site');
const isCDNBundle = api.env('bundle');
const isCommonJS = api.env('cjs');
const isESModule = api.env('esm');
const isTest = api.env('test');
if (process.env.GATSBY === 'true') { //
if (isSite) {
return {
presets: [
'babel-preset-gatsby'
@ -54,7 +57,7 @@ module.exports = api => {
development: isCommonJS
}
],
'@babel/preset-typescript'
'@babel/preset-typescript',
],
plugins: [
'@babel/plugin-proposal-optional-chaining',
@ -102,7 +105,7 @@ module.exports = api => {
// 按需引用 @see https://github.com/lodash/babel-plugin-lodash
'lodash'
// 内联 WebGL 常量 @see https://www.npmjs.com/package/babel-plugin-inline-webgl-constants
// isCDNBundle ? 'inline-webgl-constants' : {}
// isCDNBundle ? 'inline-webgl-constants' : {},
],
ignore: [
'node_modules',

View File

@ -1,7 +1,2 @@
// @ts-ignore
export * from '@l7/scene';
// @ts-ignore
export * from '@l7/layers';
// @ts-ignore
export * from '@l7/component';
export * from '@antv/l7';

View File

@ -35,9 +35,13 @@ module.exports = [
resolve: [ '.tsx', '.ts' ],
entries: [
{
find: /^@l7\/(.*)/,
replacement: resolveFile('packages/$1/src')
}
find: /^@antv\/l7-(.*)/,
replacement: resolveFile('packages/$1/src'),
},
{
find: /^@antv\/l7$/,
replacement: resolveFile('packages/l7/src'),
},
]
}
),

View File

@ -1,5 +1,5 @@
import { Scene,ImageLayer } from '@antv/l7';
import { Scene, ImageLayer } from '@antv/l7';
const scene = new Scene({
id: 'map',
pitch: 0,

View File

@ -1,5 +1,4 @@
import { Scene } from '@l7/scene';
import { RasterLayer } from '@l7/layers'
import { RasterLayer, Scene } from '@antv/l7';
import * as GeoTIFF from 'geotiff';
const scene = new Scene({
id: 'map',

View File

@ -1,6 +1,4 @@
import { Scene } from '@l7/scene';
import { PointLayer } from '@l7/layers'
import { Scale, Zoom, Layers } from '@l7/component';
import { PointLayer, Scale, Scene, Layers, Zoom } from '@antv/l7';
const scene = new Scene({
id: 'map',
pitch: 0,

View File

@ -1,5 +1,4 @@
import { Scene } from '@l7/scene';
import { Scale, Zoom } from '@l7/component';
import { Scale, Zoom, Scene } from '@antv/l7';
const scene = new Scene({
id: 'map',
pitch: 0,

View File

@ -1,6 +1,4 @@
import { Scene } from '@l7/scene';
import { PointLayer } from '@l7/layers'
import { Scale, Zoom, Layers } from '@l7/component';
import { Scale, Zoom, Scene, Layers, PointLayer } from '@antv/l7';
const scene = new Scene({
id: 'map',
pitch: 0,

View File

@ -1,5 +1,4 @@
import { Scene } from '@l7/scene';
import { LineLayer } from '@l7/layers'
import { LineLayer, Scene } from '@antv/l7';
const scene = new Scene({
id: 'map',
pitch: 0,

View File

@ -1,4 +1,4 @@
import { Scene } from '@l7/scene';
import { Scene } from '@antv/l7';
const scene = new Scene({
id: 'map',
pitch: 0,

View File

@ -1,4 +1,4 @@
import { Scene } from '@l7/scene';
import { Scene } from '@antv/l7';
const scene = new Scene({
id: 'map',
pitch: 0,

View File

@ -0,0 +1,213 @@
# IoC 容器、依赖注入与服务说明
在面向对象编程领域,[SOLID](https://en.wikipedia.org/wiki/SOLID_(object-oriented_design)) 、[“组合优于继承”](https://en.wikipedia.org/wiki/Composition_over_inheritance) 都是经典的设计原则。
IoC(Inversion of Control) 控制反转这种设计模式将对象的创建销毁、依赖关系交给容器处理,是以上设计原则的一种经典实践。其中它的一种实现 DI(Dependency Injection) 即依赖注入在工程领域应用十分广泛(下图来自 [Dependency-Injection-in-practice-CodeCAMP.pdf](http://www.mono.hr/Pdf/Dependency-Injection-in-practice-CodeCAMP.pdf)),最著名的当属 Spring
![](./screenshots/di-containers.png)
而在 JavaScript 领域,[Angular](https://angular.io/guide/dependency-injection)、[NestJS](https://docs.nestjs.com/fundamentals/custom-providers) 也都实现了自己的 IoC 容器。
L7 选择了 [InversifyJS](https://github.com/inversify/InversifyJS/blob/master/wiki/oo_design.md) 作为轻量的 IoC 容器,统一管理各类复杂的服务,实现松耦合的代码结构,同时具有以下收益:
* 提供高扩展性。
* 支持地图底图高德、Mapbox切换
* 支持渲染引擎替换
* 插件化
* 便于测试。测试用例中替换渲染引擎服务为基于 headless-gl 的渲染服务。
下图清晰的展示了切换引擎和底图时均不会影响核心代码:
![](./screenshots/packages.png)
# 多层次容器
L7 需要支持多场景(Scene),每个场景中又包含了多个图层(Layer)。不同的服务可能隶属全局、Scene 和 Layer因此对于容器也有层次化的要求。
试想如果我们只有一个全局容器,其中绑定的所有服务自然也都成了全局服务,在多场景下(页面中一个高德地图、一个 Mapbox销毁高德地图的渲染服务将影响到 Mapbox 的展示。
下图为 L7 的四个独立场景(两个高德、两个 MapboxDEMO 展示效果,它们应该是能互不干扰运行的:
![](./screenshots/multi-scene.png)
在 Angular 中也有[分层容器](https://angular.io/guide/hierarchical-dependency-injection)的应用。L7 使用的是 InversifyJS 提供的[层次化依赖注入功能](https://github.com/inversify/InversifyJS/blob/master/wiki/hierarchical_di.md)。
容器层次关系及数目如下:
```bash
RootContainer 1
-> SceneContainer 1.*
-> LayerContainer 1.*
```
其中每种容器包含不同类型的服务,这些服务有的是单例,有的是工厂方法。子容器应该能访问父容器中绑定的服务,即如果 RootContainer 已经绑定了全局日志服务SceneContainer 不需要重复绑定也能注入。
下面详细介绍下每种容器中的服务及其 API在自定义图层、自定义插件以及自定义后处理效果中都可以方便地使用这些服务。
## 全局容器
一些全局性服务不需要用户手动创建,也无需显式销毁。我们在全局容器中完成一次性的绑定,后续在所有场景、图层中都可以让容器注入这些服务的单例。类似 Angular 中的 [root ModuleInjector](https://angular.io/guide/hierarchical-dependency-injection#moduleinjector)。
例如日志、Shader 模块化服务应该是全局性的单例,我们在 `RootContainer` 完成依赖声明:
```typescript
// 在根容器中绑定日志服务为单例
rootContainer
.bind<ILogService>(TYPES.ILogService)
.to(LogService)
.inSingletonScope();
```
目前 L7 中全局性服务说明如下:
| 服务名称 | 类型 | 说明 |
| -------- | --- | --------- |
| logger | 全局服务 | 在控制台输出信息 |
* 日志服务。
* Shader 模块化服务。提供基本的 GLSL 模块化服务,基于字符串替换实现。
* 配置项校验服务。[详见](./ConfigSchemaValidation.md)
### 日志服务
基于 `probe.gl` 实现,默认只输出 debug 级别以上的日志信息。开发模式下通过设置日志等级输出 debug 信息,另外 debug 信息会带上时间戳打点,类似这样:
```bash
L7: 403ms map loaded
L7: 405ms add event listeners on canvas
L7: 676ms regenerate vertex attributes: color finished
```
通过 `logger` 引用,可使用 API 如下:
| 方法名 | 参数 | 返回值 | 说明 |
| -------- | ------------- | --------- | --------- |
| debug | `(message: string)` | 无 | 输出 debug 级别信息,会带上时间戳 |
| info | `(message: string)` | 无 | 输出 info 级别信息 |
| warn | `(message: string)` | 无 | 输出 warn 级别信息 |
| error | `(message: string)` | 无 | 输出 error 级别信息 |
在自定义图层中使用示例如下:
```typescript
class PolygonLayer extends BaseLayer<IPolygonLayerStyleOptions> {
protected renderModels() {
// 输出 debug 级别信息
this.logger.debug('start to render...');
}
}
```
### Shader 模块化服务
通过 `shaderModuleService` 引用,可使用 API 如下:
| 方法名 | 参数 | 返回值 | 说明 |
| -------- | ------------- | --------- | --------- |
| registerModule | `(moduleName: string, moduleParams: IModuleParams)` | 无 | 使用模块名和参数注册 GLSL 模块,其中 `IModuleParams` 格式见下面 |
| getModule | `(moduleName: string)` | `IModuleParams` | 根据模块名获取编译后的 GLSL 模块 |
GLSL 模块参数如下:
```typescript
interface IModuleParams {
vs: string; // vertex shader 字符串
fs: string; // fragment shader 字符串
uniforms?: { // 可选uniforms
[key: string]: IUniform;
};
}
```
我们以自定义后处理效果场景为例,完整教程见[自定义后处理效果](自定义后处理效果.md)
```typescript
protected setupShaders() {
// 使用 Shader 服务注册 GLSL 模块
this.shaderModuleService.registerModule('dotScreenEffect', {
vs: this.quad, // Vertex Shader 固定
fs: ``, // 暂时省略,在下一小节中详细介绍
});
// 使用 Shader 服务获取编译后的 GLSL 模块
const { vs, fs, uniforms } = this.shaderModuleService.getModule('dotScreenEffect');
// 使用渲染器服务获取视口尺寸
const { width, height } = this.rendererService.getViewportSize();
return {
vs,
fs,
uniforms: {
...uniforms,
u_ViewportSize: [width, height],
},
};
}
```
### 配置项校验服务
开发者不需要显式调用该服务。
Layer 子类可以通过重载 `getConfigSchema()` 方法定义自身的特有属性。例如 `PolygonLayer` 需要定义透明度,详见[ConfigSchemaValidation 使用方法](ConfigSchemaValidation.md)
```typescript
protected getConfigSchema() {
return {
properties: {
opacity: {
type: 'number',
minimum: 0,
maximum: 1,
},
},
};
}
```
以上就是供开发者使用的常见全局服务,下面我们将介绍场景容器及其内部服务。
## Scene 容器
场景可以承载多个图层,与地图底图一一对应。每个场景都有自己独立的容器确保多个场景间服务不会互相干扰,同时继承全局容器以便访问全局服务。容器内服务包括:
* 地图底图服务。每个场景有一个对应的地图底图。
* 渲染引擎服务。由于依赖 WebGL 上下文,基于 `regl` 实现。
* 图层管理服务。管理场景中所有的图层,负责图层的创建、销毁。
* PostProcessingPass。内置常用的后处理效果。
### 地图底图服务
兼容 Mapbox 和高德,开发者可以获取当前地图的状态、调用地图相机动作(缩放、平移、旋转)。
通过 `mapService` 引用。
常用地图状态获取方法如下:
| 方法名 | 参数 | 返回值 | 说明 |
| -------- | ------------- | --------- | --------- |
| getSize | 无 | `[number, number]` | 获取地图尺寸(像素单位) |
| getZoom | 无 | `number` | 获取当前地图缩放等级,以 Mapbox 为准 |
| getCenter | 无 | `{lng: number; lat: number}` | 获取当前地图中心点经纬度 |
| getPitch | 无 | `number` | 获取当前地图仰角 |
| getRotation | 无 | `number` | 获取当前地图逆时针旋转角度 |
| getBounds | 无 | `[[number, number], [number, number]]` | 获取当前地图可视区域 `[西南角、东北角]` |
⚠️对于一些地图属性将采用兼容性处理。
* 缩放等级,差异表现在:
1. 取值范围。高德缩放等级范围 `[3, 18]`,而 Mapbox 为 `[0, 20]`
2. 高德 `3` 缩放等级对应 Mapbox `2` 缩放等级。考虑兼容性,`getZoom()` 将返回 Mapbox 定义等级。
* 旋转角度。高德返回地图顺时针旋转角度Mapbox 返回逆时针旋转角度。考虑兼容性,`getRotation()` 将返回地图逆时针旋转角度。
除了获取地图状态,还可以控制地图进行一些相机动作。
### [WIP]渲染引擎服务
目前 L7 使用 [regl](https://github.com/regl-project/regl),但开发者不需要关心底层 WebGL 渲染引擎实现,即使后续更换了其他引擎,我们也将保持服务接口的稳定。
通过 `rendererService` 引用。
### 图层管理服务
开发者不需要显式调用。用于管理场景中所有的图层,负责图层的创建、销毁。
## Layer 容器
每个图层有独立的容器,同时继承自所属场景容器,自然也可以访问全局服务。
* 样式管理服务。
* MultiPassRenderer 服务。详见[MultiPassRenderer 说明](./MultiPassRenderer.md)
## 参考资料
* [动态依赖注入](https://github.com/inversify/InversifyJS/issues/1088)

View File

@ -35,7 +35,7 @@ export interface IPostProcessingPass extends IPass {
}
```
具体实现依赖 `@l7/renderer` 实现,目前使用 regl 实现 IFramebuffer 等接口。
具体实现依赖 `@antv/l7-renderer` 实现,目前使用 regl 实现 IFramebuffer 等接口。
## 内置 Pass

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 258 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 974 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 192 KiB

View File

@ -4,11 +4,10 @@ L7 提供三种使用方式CDN、Submodule 以及 React 组件。
## 通过 CDN 使用
首先在 `<head>` 中引入 L7 CDN 版本的 JS 和 CSS 文件:
首先在 `<head>` 中引入 L7 CDN 版本的 JS 文件:
```html
<head>
<script src='https://api.l7/v2.0.0-beta/l7.js'></script>
<link href='https://api.l7/v2.0.0-beta/l7.css' rel='stylesheet' />
</head>
```
@ -79,8 +78,6 @@ L7 提供三种使用方式CDN、Submodule 以及 React 组件。
// 添加图层到场景中
scene.addLayer(layer);
// 渲染场景
scene.render();
})();
</script>
</body>
@ -92,17 +89,16 @@ L7 提供三种使用方式CDN、Submodule 以及 React 组件。
## 通过 Submodule 使用
首先通过 `npm/yarn` 安装 `@l7/scene` 和 `@l7/layers`
首先通过 `npm/yarn` 安装 `@antv/l7`
```bash
npm install --save @l7/scene @l7/layers
npm install --save @antv/l7
// or
yarn add @l7/scene @l7/layers
yarn add @antv/l7
```
然后就可以使用其中包含的场景和各类图层:
```typescript
import { Scene } from '@l7/scene';
import { PolygonLayer } from '@l7/layers';
import { Scene, PolygonLayer } from '@antv/l7';
(async function() {
// 获取数据
@ -142,19 +138,9 @@ import { PolygonLayer } from '@l7/layers';
// 添加图层到场景中
scene.addLayer(layer);
// 渲染场景
scene.render();
})();
```
最后在 `<head>` 中引入 L7 CDN 版本的 CSS 文件:
```html
<head>
<link href='https://api.l7/v2.0.0-beta/l7.css' rel='stylesheet' />
</head>
```
L7 目前的文档都通过这种方式使用,可以参考项目中的 stories
* [高德地图](https://github.com/antvis/L7/blob/next/stories/MapAdaptor/components/AMap.tsx)
* [Mapbox](https://github.com/antvis/L7/blob/next/stories/MapAdaptor/components/Mapbox.tsx)
@ -164,8 +150,7 @@ L7 目前的文档都通过这种方式使用,可以参考项目中的 stories
React 组件待开发,目前可以暂时以 Submodule 方式使用:
```tsx
import { Scene } from '@l7/scene';
import { PolygonLayer } from '@l7/layers';
import { Scene, PolygonLayer } from '@antv/l7';
import * as React from 'react';
export default class AMap extends React.Component {
@ -206,7 +191,6 @@ export default class AMap extends React.Component {
opacity: 0.8,
});
scene.addLayer(layer);
scene.render();
this.scene = scene;
}

View File

@ -11,19 +11,19 @@
因此目前和 Mapbox 做法一样,我们使用 Rollup 构建 CDN Bundler。
打包命令如下,会在 `dist/` 下输出产物:
打包命令如下,会在 `packages/l7/dist` 下输出产物:
```bash
yarn bundle
```
### UMD
以 L7 为命名空间,让用户可以通过类似 `L7.Scene` 的方式使用。同时以 UMD 为构建目标:
以 L7 为命名空间,让用户可以通过类似 `L7.Scene` 的方式使用。同时以 UMD 为构建目标,输出到 `packages/l7/dist`
```javascript
{
input: resolveFile('build/bundle.ts'),
output: {
file: resolveFile('dist/bundle.js'),
file: resolveFile('packages/l7/dist/bundle.js'),
format: 'umd',
name: 'L7',
},
@ -33,13 +33,12 @@ yarn bundle
目前只需要暴露场景以及图层相关的 API因此 Bundler 非常简单:
```typescript
// build/bundle.ts
export * from '@l7/scene';
export * from '@l7/layers';
export * from '@antv/l7';
```
### Alias
为了帮助 resolver 定位 lerna packages需要重命名类似 `@l7/scene` 这样的依赖路径:
为了帮助 resolver 定位 lerna packages需要重命名类似 `@antv/l7-scene` 这样的依赖路径:
```javascript
import alias from '@rollup/plugin-alias';
@ -314,9 +313,36 @@ yarn build
},
```
### [WIP] 生成 TS 声明文件
### 生成 TS 声明文件
和构建前类型检查不同,此时我们需要 tsc 输出类型声明文件了,当然不需要包含 story 和测试用例。
和构建前类型检查不同,此时我们需要 tsc 输出类型声明文件了:
```json
{
"postbuild": "yarn build:declarations",
"build:declarations": "lerna exec --stream --no-bail 'tsc --project ./tsconfig.build.json'"
}
```
当然不需要包含 story 和测试用例:
```json
{
"extends": "./tsconfig.json",
"compilerOptions": {
"noEmit": false,
"emitDeclarationOnly": true,
"declaration": true,
"rootDir": "./",
"baseUrl": "./",
"paths": {
"@antv/l7-*": ["packages/*/src"],
"@antv/l7": ["packages/l7/src"],
"*": ["node_modules", "packages"]
}
},
"exclude": ["**/*.story.*", "**/__tests__/**/*", "**/*.spec.*", "dist"],
"include": []
}
```
### [WIP] 异步加载 Mapbox

View File

@ -68,7 +68,7 @@ module.exports = {
### 单元测试
这类测试直接使用 Jest API 就好了,我们以 `@l7/core` 模块的 `ShaderModuleService` 为例,编写一个简单的测试用例:
这类测试直接使用 Jest API 就好了,我们以 `@antv/l7-core` 模块的 `ShaderModuleService` 为例,编写一个简单的测试用例:
```typescript
// services/shader/__test__/shader.spec.ts

View File

@ -0,0 +1,174 @@
# 自定义后处理效果
L7 自定义了一套较为灵活的渲染管线,在后处理效果方面提供了最大程度的扩展性。除了直接使用内置的常用效果(例如下图中 ColorHalftone、六边形像素化、噪声、Sepia开发者还可以自定义任何后处理效果在场景中完成注册即可应用到任意图层上。
![](./screenshots/multi-scene.png)
下面我们以 [glfx.js](http://evanw.github.io/glfx.js/demo/) 中的 Dot Screen 效果(下图)为例,介绍如何在 PolygonLayer 中应用这种效果。完整 DEMO 代码[在此]()。
![](./screenshots/custom-effect.png)
我们将分成三步介绍:
1. 定义效果
* 定义效果参数
* 继承后处理效果基类
* 编写 Fragment Shader
2. 在场景中注册效果
3. 在图层中使用效果
## 定义效果
### 定义效果参数
实现任何一种后处理效果,我们都希望提供一些灵活的参数,可以在运行时供使用者修改。以我们需要实现的 Dot Screen 效果为例,参数接口 `IDotScreenEffectConfig` 定义如下:
```typescript
interface IDotScreenEffectConfig {
center: [number, number]; // pattern 圆心
angle: number; // dot 旋转角度
size: number; // dot 尺寸
}
```
### 继承后处理效果基类
为了最大程度减少样板代码L7 提供了 `BasePostProcessingPass` 基类供子类继承,同时通过泛型将上一步定义的参数接口传入:
```typescript
import { BasePostProcessingPass } from '@antv/l7';
class DotScreenEffect extends BasePostProcessingPass<IDotScreenEffectConfig> {
//... 省略重载方法
}
```
接下来我们只需要重载基类的一个方法 `setupShaders`。在这个方法中我们可以使用 L7 内置的服务,例如 Shader 服务、渲染服务等,各服务说明及 API 使用方式[详见](./IoC%20容器、依赖注入与服务说明.md)。
```typescript
protected setupShaders() {
// 使用 Shader 服务注册 GLSL 模块
this.shaderModuleService.registerModule('dotScreenEffect', {
vs: this.quad, // Vertex Shader 固定
fs: ``, // 暂时省略,在下一小节中详细介绍
});
// 使用 Shader 服务获取编译后的 GLSL 模块
const { vs, fs, uniforms } = this.shaderModuleService.getModule('dotScreenEffect');
// 使用渲染器服务获取视口尺寸
const { width, height } = this.rendererService.getViewportSize();
return {
vs,
fs,
uniforms: {
...uniforms,
u_ViewportSize: [width, height],
},
};
}
```
### 编写 Fragment Shader
在编写 Fragment Shader 时,可以按照如下模版。由于 L7 实现了简单的 GLSL 模块化,可以使用一些特殊的语法,例如:
* Uniform 设置默认值
* 引入 L7 内置 GLSL 模块
```glsl
varying vec2 v_UV;
uniform sampler2D u_Texture;
uniform vec2 u_ViewportSize : [1.0, 1.0];
// 自定义效果参数声明
// 自定义效果函数 myCustomEffect 定义
void main() {
// 纹理采样
gl_FragColor = vec4(texture2D(u_Texture, v_UV));
// 应用自定义效果函数
gl_FragColor = myCustomEffect(gl_FragColor, u_ViewportSize, v_UV);
}
```
Dot Screen 效果具体 GLSL 代码可以参考 [luma.gl](https://github.com/uber/luma.gl/blob/master/modules/engine/src/effects/shader-modules/fun-filters/dotscreen.js#L11-L30)。
完整 Fragment Shader 代码如下。需要注意的是这里 Uniform 名需要和效果配置项属性名统一,即 `size` 对应 `u_Size`、`angle` 对应 `u_Angle`。:
```glsl
varying vec2 v_UV;
uniform sampler2D u_Texture;
uniform vec2 u_ViewportSize : [1.0, 1.0];
// 自定义效果参数及默认值声明
uniform vec2 u_Center : [0.5, 0.5];
uniform float u_Angle : 1;
uniform float u_Size : 3;
// 自定义效果实现
// @see https://github.com/uber/luma.gl/blob/master/modules/engine/src/effects/shader-modules/fun-filters/dotscreen.js#L11-L30
float pattern(vec2 texSize, vec2 texCoord) {
float scale = 3.1415 / u_Size;
float s = sin(u_Angle), c = cos(u_Angle);
vec2 tex = texCoord * texSize - u_Center * texSize;
vec2 point = vec2(
c * tex.x - s * tex.y,
s * tex.x + c * tex.y
) * scale;
return (sin(point.x) * sin(point.y)) * 4.0;
}
vec4 dotScreen_filterColor(vec4 color, vec2 texSize, vec2 texCoord) {
float average = (color.r + color.g + color.b) / 3.0;
return vec4(vec3(average * 10.0 - 5.0 + pattern(texSize, texCoord)), color.a);
}
void main() {
gl_FragColor = vec4(texture2D(u_Texture, v_UV));
gl_FragColor = dotScreen_filterColor(gl_FragColor, u_ViewportSize, v_UV);
}
```
至此我们就完成了效果的定义。
## 在场景中注册
一种效果要想生效,必须先在场景中完成注册。`scene.registerPostProcessingPass()` 接受两个参数分别为上一步定义的效果构造函数以及效果名。
后续我们在图层中使用时就可以通过效果名引用了:
```typescript
// 场景定义
const scene = new Scene({
id: 'map',
type: 'mapbox',
style: 'mapbox://styles/mapbox/streets-v9',
center: [110.19382669582967, 50.258134],
pitch: 0,
zoom: 3,
});
// 注册自定义后处理效果
scene.registerPostProcessingPass(
DotScreenEffect, // 效果构造函数
'dotScreenEffect', // 效果名,便于后续在图层中引用
);
```
## 在图层中使用效果
和 L7 内置的后处理效果使用方法一致,通过效果名引用,同时传入定义参数即可:
```typescript
const layer = new PolygonLayer({
enableMultiPassRenderer: true,
enablePicking: true,
enableHighlight: true,
passes: [
[
'dotScreenEffect', // 引用效果名
{
size: 8, // 传入参数
angle: 1,
},
],
],
});
```
最终效果如下:
![](./screenshots/dotscreen.png)

View File

@ -7,7 +7,24 @@ order: 1
## Scene
L7 地理可视化 地图图层组件以及可视化所需要的资源如图片字体通过Scene统一管理
```javascript
// Module 引用
import { Scene } from '@antv/l7';
const scene = new Scene({
id: 'map',
mapStyle: 'dark',
center: [ 110.770672, 34.159869 ],
pitch: 45,
});
// CDN 使用方法
const scene = new L7.Scene({
id: 'map',
mapStyle: 'dark',
center: [ 110.770672, 34.159869 ],
pitch: 45,
});
```
## Map

View File

@ -29,20 +29,28 @@ L7 地理可视化 地图,图层,组件,以及可视化所需要的资源
const map = scene.map
```
为了统一不底图之前的接口差异 L7 在scene层对map的方法做了统一因此一些地图的操作方法可以通过scene调用这样切换不同底图时保证表现一致。
为了统一不底图之前的接口差异 L7 在scene层对map的方法做了统一因此一些地图的操作方法可以通过scene调用这样切换不同底图时保证表现一致。
示例代码
```javascript
import {Scene} from '@antv/scene';
const scene =new L7.Scene({
id:'map',
mapStyle:'dark',
center:[ 110.770672, 34.159869 ],
pitch:45
})
```
// Module 引用
import { Scene } from '@antv/l7';
const scene = new Scene({
id: 'map',
mapStyle: 'dark',
center: [ 110.770672, 34.159869 ],
pitch: 45,
});
// CDN 使用方法
const scene = new L7.Scene({
id: 'map',
mapStyle: 'dark',
center: [ 110.770672, 34.159869 ],
pitch: 45,
});
```
### 构造函数

View File

@ -1,4 +1,3 @@
import { Scene, PolygonLayer, LineLayer } from '@antv/l7';
const scene = new Scene({
id: 'map',

View File

@ -19,6 +19,3 @@ layer.source(
}
);
scene.addLayer(layer);
// scene.on('loaded', () => {
// scene.addLayer(layer);
// });

View File

@ -1,9 +1,6 @@
require('./site/css/demo.css');
require('./packages/component/src/css/l7.css');
// window.geotiff = require('geotiff/dist/geotiff.bundle.min.js');
// window.scene = require('./packages/scene/src');
// window.layers = require('./packages/layers/src');
// window.component = require('./packages/component/src');
window.geotiff = require('geotiff/dist/geotiff.bundle.min.js');
window.g2 = require('@antv/g2');
window.l7 = require('@antv/l7');

View File

@ -156,7 +156,10 @@ module.exports = {
}
],
playground: {
container: '<div style="min-height: 500px; justify-content: center;position: relative" id="map"/>'
container: '<div style="min-height: 500px; justify-content: center;position: relative" id="map"/>',
dependencies: {
'@antv/l7': 'beta'
}
}
}
};

View File

@ -4,14 +4,14 @@ exports.onCreateWebpackConfig = ({ getConfig }) => {
config.resolve.extensions.push('.glsl');
config.resolve.alias = {
...config.resolve.alias,
'@l7/core': path.resolve(__dirname, 'packages/core/src'),
'@l7/component': path.resolve(__dirname, 'packages/component/src'),
'@l7/layers': path.resolve(__dirname, 'packages/layers/src'),
'@l7/maps': path.resolve(__dirname, 'packages/maps/src'),
'@l7/renderer': path.resolve(__dirname, 'packages/renderer/src'),
'@l7/scene': path.resolve(__dirname, 'packages/scene/src'),
'@l7/source': path.resolve(__dirname, 'packages/source/src'),
'@l7/utils': path.resolve(__dirname, 'packages/utils/src'),
'@antv/l7': path.resolve(__dirname, 'packages/l7/src')
'@antv/l7': path.resolve(__dirname, 'packages/l7/src'),
'@antv/l7-core': path.resolve(__dirname, 'packages/core/src'),
'@antv/l7-component': path.resolve(__dirname, 'packages/component/src'),
'@antv/l7-layers': path.resolve(__dirname, 'packages/layers/src'),
'@antv/l7-maps': path.resolve(__dirname, 'packages/maps/src'),
'@antv/l7-renderer': path.resolve(__dirname, 'packages/renderer/src'),
'@antv/l7-scene': path.resolve(__dirname, 'packages/scene/src'),
'@antv/l7-source': path.resolve(__dirname, 'packages/source/src'),
'@antv/l7-utils': path.resolve(__dirname, 'packages/utils/src'),
};
};

View File

@ -26,7 +26,7 @@ module.exports = {
moduleFileExtensions: [ 'ts', 'tsx', 'js' ],
modulePathIgnorePatterns: [ 'dist' ],
moduleNameMapper: {
'@l7/(.+)$': '<rootDir>packages/$1/src'
'@antv/l7-(.+)$': '<rootDir>packages/$1/src',
},
notify: true,
notifyMode: 'always',

View File

@ -4,16 +4,19 @@
],
"command": {
"publish": {
"ignoreChanges": ["*.md"],
"ignoreChanges": [
"*.md"
],
"allowBranch": [
"master",
"develop",
"next"
"next",
"dev-container"
],
"message": "chore: publish"
}
},
"version": "0.0.1",
"version": "2.0.0-beta.10",
"npmClient": "yarn",
"useWorkspaces": true,
"publishConfig": {

View File

@ -6,7 +6,7 @@
},
"devDependencies": {
"@antv/g2": "^3.5.9",
"@antv/gatsby-theme-antv": "^0.9.92",
"@antv/gatsby-theme-antv": "0.10.1",
"@babel/cli": "^7.6.4",
"@babel/core": "^7.6.4",
"@babel/plugin-proposal-decorators": "^7.6.0",
@ -72,6 +72,7 @@
"jest-styled-components": "^6.2.1",
"lerna": "^3.16.4",
"lint-staged": "^9.2.4",
"load-styles": "^2.0.0",
"node-sass": "^4.12.0",
"npm-run-all": "^4.1.5",
"postcss": "^7.0.18",
@ -114,8 +115,8 @@
},
"scripts": {
"start": "npm run site:develop",
"site:develop": "GATSBY=true gatsby develop --open -H 0.0.0.0",
"site:build": "npm run site:clean && GATSBY=true gatsby build --prefix-paths",
"site:develop": "BABEL_ENV=site gatsby develop --open -H 0.0.0.0",
"site:build": "npm run site:clean && BABEL_ENV=site gatsby build --prefix-paths",
"site:clean": "gatsby clean",
"site:deploy": "npm run site:build && gh-pages -d public",
"site:publish": "gh-pages -d public",
@ -123,7 +124,8 @@
"lint:site": "eslint examples/**/**/*.js --fix",
"prebuild": "run-p tsc lint",
"build": "yarn clean && lerna run build",
"todo:postbuild": "yarn build:declarations",
"postbuild": "yarn build:declarations",
"build:declarations": "lerna exec --stream --no-bail 'tsc --project ./tsconfig.build.json'",
"fix": "run-p -c 'lint:ts-* --fix'",
"lint:css": "stylelint 'packages/**/*.js{,x}'",
"lint:ts-prod": "tslint --config tslint.prod.json 'packages/**/*.ts{,x}'",
@ -132,13 +134,13 @@
"lint": "run-p -c lint:*",
"commit": "git-cz",
"version": "lerna version --conventional-commits",
"prerelease": "yarn build",
"version:prerelease": "lerna version --conventional-prerelease",
"prerelease": "yarn build && yarn bundle",
"release": "lerna publish from-package --registry https://registry.npmjs.com/",
"storybook": "start-storybook -p 6006",
"test": "BABEL_ENV=test jest",
"coveralls": "jest --coverage && cat ./tests/coverage/lcov.info | coveralls",
"tsc": "tsc",
"build:declarations": "lerna exec --stream --no-bail 'tsc --project ./tsconfig.build.json'",
"watch": "yarn clean && lerna exec --parallel 'BABEL_ENV=cjs babel --watch src --root-mode upward --out-dir lib --source-maps --extensions .ts,.tsx --delete-dir-on-start --no-comments'",
"bundle": "BABEL_ENV=bundle node_modules/.bin/rollup -c ./build/rollup.config.js",
"bundle:watch": "BABEL_ENV=bundle node_modules/.bin/rollup -c ./build/rollup.config.js --watch",

View File

@ -1,6 +1,6 @@
{
"name": "@l7/component",
"version": "0.0.1",
"name": "@antv/l7-component",
"version": "2.0.0-beta.10",
"description": "",
"main": "lib/index.js",
"module": "es/index.js",
@ -24,13 +24,14 @@
"author": "lzxue",
"license": "ISC",
"dependencies": {
"@l7/core": "0.0.1",
"@l7/utils": "0.0.1",
"@antv/l7-core": "^2.0.0-beta.10",
"@antv/l7-utils": "^2.0.0-beta.10",
"@turf/distance": "^6.0.1",
"eventemitter3": "^4.0.0",
"inversify": "^5.0.1",
"inversify-inject-decorators": "^3.1.0",
"inversify-logging": "^0.2.1"
"inversify-logging": "^0.2.1",
"load-styles": "^2.0.0"
},
"gitHead": "0563f357f3a07c099bf1ffa9350e6fa3c88353ae",
"publishConfig": {

View File

@ -3,11 +3,11 @@ import {
ILayerService,
IMapService,
IRendererService,
lazyInject,
TYPES,
} from '@l7/core';
import { DOM } from '@l7/utils';
} from '@antv/l7-core';
import { DOM } from '@antv/l7-utils';
import { EventEmitter } from 'eventemitter3';
import { Container } from 'inversify';
export enum PositionType {
'TOPRIGHT' = 'topright',
@ -26,15 +26,11 @@ export interface IControlOption {
}
export default class Control extends EventEmitter {
public controlOption: IControlOption;
protected mapsService: IMapService;
protected container: HTMLElement;
@lazyInject(TYPES.IRendererService)
protected readonly renderService: IRendererService;
@lazyInject(TYPES.ILayerService)
protected readonly layerService: ILayerService;
@lazyInject(TYPES.IControlService)
private readonly controlService: IControlService;
protected mapsService: IMapService;
protected renderService: IRendererService;
protected layerService: ILayerService;
protected controlService: IControlService;
private isShow: boolean;
@ -45,27 +41,36 @@ export default class Control extends EventEmitter {
...(cfg || {}),
};
}
public getDefault() {
return {
position: PositionType.TOPRIGHT,
};
}
public setPosition(position: PositionName) {
const controlService = this.controlService;
if (controlService) {
controlService.removeControl(this);
}
this.controlOption.position = position;
if (controlService) {
controlService.addControl(this, this.mapsService);
}
// FIXME: 只是改变位置不需要销毁再重建吧
// const controlService = this.controlService;
// if (controlService) {
// controlService.removeControl(this);
// }
// this.controlOption.position = position;
// if (controlService) {
// controlService.addControl(this, this.mapsService);
// }
return this;
}
public addTo(mapService: IMapService) {
this.remove();
public addTo(sceneContainer: Container) {
this.mapsService = sceneContainer.get<IMapService>(TYPES.IMapService);
this.renderService = sceneContainer.get<IRendererService>(
TYPES.IRendererService,
);
this.layerService = sceneContainer.get<ILayerService>(TYPES.ILayerService);
this.controlService = sceneContainer.get<IControlService>(
TYPES.IControlService,
);
this.isShow = true;
this.mapsService = mapService;
this.container = this.onAdd(mapService);
this.container = this.onAdd();
const container = this.container;
const pos = this.controlOption.position;
const corner = this.controlService.controlCorners[pos];
@ -78,7 +83,7 @@ export default class Control extends EventEmitter {
}
return this;
}
public onAdd(Map: IMapService): HTMLElement {
public onAdd(): HTMLElement {
throw new Error('Method not implemented.');
}
public hide() {

View File

@ -1,5 +1,5 @@
import { IMapService } from '@l7/core';
import { bindAll, DOM, lnglatDistance } from '@l7/utils';
import { IMapService } from '@antv/l7-core';
import { bindAll, DOM, lnglatDistance } from '@antv/l7-utils';
import Control, {
IControlOption,
PositionName,
@ -63,7 +63,7 @@ export default class Layers extends Control {
sortLayers: false,
};
}
public onAdd(MapService: IMapService) {
public onAdd() {
this.initLayout();
this.update();
this.mapsService.on('zoomend', this.checkDisabledLayers);
@ -177,7 +177,7 @@ export default class Layers extends Control {
input = inputs[i];
layer = this.layerService.getLayer(input.layerId);
if (layer) {
input.disabled = layer.visible && !layer.isVisible();
input.disabled = !layer.isVisible();
}
}
}

View File

@ -1,13 +1,12 @@
import { IMapService } from '@l7/core';
import { bindAll, DOM } from '@l7/utils';
import Control, { IControlOption, PositionType } from './BaseControl';
import { DOM } from '@antv/l7-utils';
import Control, { PositionType } from './BaseControl';
export default class Logo extends Control {
public getDefault() {
return {
position: PositionType.BOTTOMLEFT,
};
}
public onAdd(MapService: IMapService) {
public onAdd() {
const className = 'l7-control-logo';
const container = DOM.create('div', className);
const anchor: HTMLLinkElement = DOM.create(

View File

@ -1,5 +1,5 @@
import { IMapService } from '@l7/core';
import { bindAll, DOM, lnglatDistance } from '@l7/utils';
import { IMapService } from '@antv/l7-core';
import { bindAll, DOM, lnglatDistance } from '@antv/l7-utils';
import Control, { IControlOption, PositionType } from './BaseControl';
export interface IScaleControlOption extends IControlOption {
maxWidth: number;
@ -25,7 +25,7 @@ export default class Scale extends Control {
};
}
public onAdd(MapService: IMapService) {
public onAdd() {
const className = 'l7-control-scale';
const container = DOM.create('div', className);
this.addScales(className + '-line', container);
@ -36,7 +36,7 @@ export default class Scale extends Control {
return container;
}
public onRemove(MapService: IMapService) {
public onRemove() {
const { updateWhenIdle } = this.controlOption;
this.mapsService.off(updateWhenIdle ? 'moveend' : 'mapmove', this.update);
}

View File

@ -1,5 +1,4 @@
import { IMapService } from '@l7/core';
import { bindAll, DOM } from '@l7/utils';
import { bindAll, DOM } from '@antv/l7-utils';
import Control, { IControlOption, PositionType } from './BaseControl';
export interface IZoomControlOption extends IControlOption {
zoomInText: string;
@ -26,7 +25,7 @@ export default class Zoom extends Control {
};
}
public onAdd(MapService: IMapService) {
public onAdd() {
const zoomName = 'l7-control-zoom';
const container = DOM.create('div', zoomName + ' l7-bar');

View File

@ -1,5 +1,5 @@
import { ILngLat, IMapService, IMarkerScene, IPopup } from '@l7/core';
import { bindAll, DOM } from '@l7/utils';
import { ILngLat, IMapService, IMarkerScene, IPopup } from '@antv/l7-core';
import { bindAll, DOM } from '@antv/l7-utils';
import { anchorTranslate, anchorType, applyAnchorClass } from './utils/anchor';
// marker 支持 dragger 未完成

View File

@ -1,5 +1,5 @@
import { ILngLat, IMapService, IMarkerScene, IPopup } from '@l7/core';
import { bindAll, DOM } from '@l7/utils';
import { ILngLat, IMapService, IMarkerScene, IPopup } from '@antv/l7-core';
import { bindAll, DOM } from '@antv/l7-utils';
import { EventEmitter } from 'eventemitter3';
import { anchorTranslate, anchorType, applyAnchorClass } from './utils/anchor';

View File

@ -1,6 +1,6 @@
{
"name": "@l7/core",
"version": "0.0.1",
"name": "@antv/l7-core",
"version": "2.0.0-beta.10",
"description": "",
"main": "lib/index.js",
"module": "es/index.js",
@ -22,7 +22,7 @@
"author": "xiaoiver",
"license": "ISC",
"dependencies": {
"@l7/utils": "0.0.1",
"@antv/l7-utils": "^2.0.0-beta.10",
"@mapbox/tiny-sdf": "^1.1.1",
"ajv": "^6.10.2",
"eventemitter3": "^4.0.0",
@ -39,6 +39,7 @@
"viewport-mercator-project": "^6.2.1"
},
"devDependencies": {
"@types/amap-js-api": "^1.4.6",
"@types/gl-matrix": "^2.4.5",
"@types/hammerjs": "^2.0.36",
"@types/lodash": "^4.14.138",

View File

@ -1,9 +1,10 @@
import container, { lazyInject, lazyMultiInject } from './inversify.config';
import ClearPass from './services/renderer/passes/ClearPass';
import MultiPassRenderer from './services/renderer/passes/MultiPassRenderer';
import PixelPickingPass from './services/renderer/passes/PixelPickingPass';
import RenderPass from './services/renderer/passes/RenderPass';
import TAAPass from './services/renderer/passes/TAAPass';
import container, {
createLayerContainer,
createSceneContainer,
lazyInject,
lazyMultiInject,
} from './inversify.config';
import BasePostProcessingPass from './services/renderer/passes/BasePostProcessingPass';
import { TYPES } from './types';
import { packCircleVertex } from './utils/vertex-compression';
@ -12,6 +13,8 @@ export {
* IoC
*/
container,
createSceneContainer,
createLayerContainer,
/**
* lazy inject Layer 使
*/
@ -22,12 +25,7 @@ export {
*/
TYPES,
packCircleVertex,
/** pass */
MultiPassRenderer,
ClearPass,
RenderPass,
PixelPickingPass,
TAAPass,
BasePostProcessingPass,
};
/** 暴露服务接口供其他 packages 实现 */

View File

@ -1,3 +1,7 @@
/**
* Root Container
* @see /dev-docs/IoC .md
*/
import 'reflect-metadata';
import { EventEmitter } from 'eventemitter3';
@ -34,7 +38,15 @@ import SceneService from './services/scene/SceneService';
import ShaderModuleService from './services/shader/ShaderModuleService';
/** PostProcessing passes */
import { IPostProcessingPass } from './services/renderer/IMultiPassRenderer';
import {
IMultiPassRenderer,
IPass,
IPostProcessingPass,
IPostProcessor,
} from './services/renderer/IMultiPassRenderer';
import ClearPass from './services/renderer/passes/ClearPass';
import MultiPassRenderer from './services/renderer/passes/MultiPassRenderer';
import PixelPickingPass from './services/renderer/passes/PixelPickingPass';
import BlurHPass from './services/renderer/passes/post-processing/BlurHPass';
import BlurVPass from './services/renderer/passes/post-processing/BlurVPass';
import ColorHalfTonePass from './services/renderer/passes/post-processing/ColorHalfTonePass';
@ -43,37 +55,20 @@ import HexagonalPixelatePass from './services/renderer/passes/post-processing/He
import InkPass from './services/renderer/passes/post-processing/InkPass';
import NoisePass from './services/renderer/passes/post-processing/NoisePass';
import SepiaPass from './services/renderer/passes/post-processing/SepiaPass';
import PostProcessor from './services/renderer/passes/PostProcessor';
import RenderPass from './services/renderer/passes/RenderPass';
import TAAPass from './services/renderer/passes/TAAPass';
// @see https://github.com/inversify/InversifyJS/blob/master/wiki/container_api.md#defaultscope
const container = new Container();
/**
* bind services
* bind global services in root container
*/
container
.bind<ISceneService>(TYPES.ISceneService)
.to(SceneService)
.inTransientScope();
container
.bind<IGlobalConfigService>(TYPES.IGlobalConfigService)
.to(GlobalConfigService)
.inSingletonScope();
container
.bind<ILayerService>(TYPES.ILayerService)
.to(LayerService)
.inSingletonScope();
container
.bind<IStyleAttributeService>(TYPES.IStyleAttributeService)
.to(StyleAttributeService)
.inRequestScope();
container
.bind<ICameraService>(TYPES.ICameraService)
.to(CameraService)
.inSingletonScope();
container
.bind<ICoordinateSystemService>(TYPES.ICoordinateSystemService)
.to(CoordinateSystemService)
.inSingletonScope();
container
.bind<IIconService>(TYPES.IIconService)
.to(IconService)
@ -86,18 +81,10 @@ container
.bind<ILogService>(TYPES.ILogService)
.to(LogService)
.inSingletonScope();
container
.bind<IInteractionService>(TYPES.IInteractionService)
.to(InteractionService)
.inSingletonScope();
container
.bind<IFontService>(TYPES.IFontService)
.to(FontService)
.inSingletonScope();
container
.bind<IControlService>(TYPES.IControlService)
.to(ControlService)
.inSingletonScope();
// @see https://github.com/inversify/InversifyJS/blob/master/wiki/inheritance.md#what-can-i-do-when-my-base-class-is-provided-by-a-third-party-module
decorate(injectable(), EventEmitter);
@ -157,49 +144,134 @@ export const lazyMultiInject = (
};
};
// 绑定 post processing passes
container
export default container;
let sceneIdCounter = 0;
export function createSceneContainer() {
// @see https://github.com/inversify/InversifyJS/blob/master/wiki/hierarchical_di.md#support-for-hierarchical-di-systems
const sceneContainer = new Container();
sceneContainer.parent = container;
// 生成场景 ID 并保存在容器内
sceneContainer
.bind<string>(TYPES.SceneID)
.toConstantValue(`${sceneIdCounter++}`);
sceneContainer
.bind<ILayerService>(TYPES.ILayerService)
.to(LayerService)
.inSingletonScope();
sceneContainer
.bind<ISceneService>(TYPES.ISceneService)
.to(SceneService)
.inSingletonScope();
sceneContainer
.bind<ICameraService>(TYPES.ICameraService)
.to(CameraService)
.inSingletonScope();
sceneContainer
.bind<ICoordinateSystemService>(TYPES.ICoordinateSystemService)
.to(CoordinateSystemService)
.inSingletonScope();
sceneContainer
.bind<IInteractionService>(TYPES.IInteractionService)
.to(InteractionService)
.inSingletonScope();
sceneContainer
.bind<IControlService>(TYPES.IControlService)
.to(ControlService)
.inSingletonScope();
// 绑定常规 passes
sceneContainer
.bind<IPass<unknown>>(TYPES.INormalPass)
.to(ClearPass)
.whenTargetNamed('clear');
sceneContainer
.bind<IPass<unknown>>(TYPES.INormalPass)
.to(PixelPickingPass)
.whenTargetNamed('pixelPicking');
sceneContainer
.bind<IPass<unknown>>(TYPES.INormalPass)
.to(RenderPass)
.whenTargetNamed('render');
sceneContainer
.bind<IPass<unknown>>(TYPES.INormalPass)
.to(TAAPass)
.whenTargetNamed('taa');
sceneContainer
.bind<interfaces.Factory<IPass<unknown>>>(TYPES.IFactoryNormalPass)
.toFactory<IPass<unknown>>((context) => (named: string) =>
context.container.getNamed<IPass<unknown>>(TYPES.INormalPass, named),
);
// 绑定 post processing passes
sceneContainer
.bind<IPostProcessingPass<unknown>>(TYPES.IPostProcessingPass)
.to(CopyPass)
.whenTargetNamed('copy');
container
sceneContainer
.bind<IPostProcessingPass<unknown>>(TYPES.IPostProcessingPass)
.to(BlurHPass)
.whenTargetNamed('blurH');
container
sceneContainer
.bind<IPostProcessingPass<unknown>>(TYPES.IPostProcessingPass)
.to(BlurVPass)
.whenTargetNamed('blurV');
container
sceneContainer
.bind<IPostProcessingPass<unknown>>(TYPES.IPostProcessingPass)
.to(NoisePass)
.whenTargetNamed('noise');
container
sceneContainer
.bind<IPostProcessingPass<unknown>>(TYPES.IPostProcessingPass)
.to(SepiaPass)
.whenTargetNamed('sepia');
container
sceneContainer
.bind<IPostProcessingPass<unknown>>(TYPES.IPostProcessingPass)
.to(ColorHalfTonePass)
.whenTargetNamed('colorHalftone');
container
sceneContainer
.bind<IPostProcessingPass<unknown>>(TYPES.IPostProcessingPass)
.to(HexagonalPixelatePass)
.whenTargetNamed('hexagonalPixelate');
container
sceneContainer
.bind<IPostProcessingPass<unknown>>(TYPES.IPostProcessingPass)
.to(InkPass)
.whenTargetNamed('ink');
container
// 绑定工厂方法
sceneContainer
.bind<interfaces.Factory<IPostProcessingPass<unknown>>>(
TYPES.IFactoryPostProcessingPass,
)
.toFactory<IPostProcessingPass<unknown>>((context) => (named: string) =>
context.container.getNamed<IPostProcessingPass<unknown>>(
.toFactory<IPostProcessingPass<unknown>>((context) => (named: string) => {
const pass = context.container.getNamed<IPostProcessingPass<unknown>>(
TYPES.IPostProcessingPass,
named,
),
);
pass.setName(named);
return pass;
});
export default container;
return sceneContainer;
}
export function createLayerContainer(sceneContainer: Container) {
const layerContainer = new Container();
layerContainer.parent = sceneContainer;
layerContainer
.bind<IStyleAttributeService>(TYPES.IStyleAttributeService)
.to(StyleAttributeService)
.inSingletonScope();
layerContainer
.bind<IMultiPassRenderer>(TYPES.IMultiPassRenderer)
.to(MultiPassRenderer)
.inSingletonScope();
layerContainer
.bind<IPostProcessor>(TYPES.IPostProcessor)
.to(PostProcessor)
.inSingletonScope();
return layerContainer;
}

View File

@ -1,4 +1,4 @@
import { LRUCache } from '@l7/utils';
import { LRUCache } from '@antv/l7-utils';
// @ts-ignore
import TinySDF from '@mapbox/tiny-sdf';
import { inject, injectable } from 'inversify';

View File

@ -1,6 +1,5 @@
import { DOM } from '@l7/utils';
import { inject, injectable } from 'inversify';
import { IMapService } from '../map/IMapService';
import { DOM } from '@antv/l7-utils';
import { Container, injectable } from 'inversify';
import {
IControl,
IControlCorners,
@ -15,13 +14,12 @@ export default class ControlService implements IControlService {
public controlContainer: HTMLElement;
private controls: IControl[] = [];
public init(cfg: IControlServiceCfg) {
this.destroy();
this.container = cfg.container;
this.initControlPos();
}
public addControl(ctr: IControl, mapService: IMapService): void {
ctr.addTo(mapService); // scene对象
public addControl(ctr: IControl, sceneContainer: Container): void {
ctr.addTo(sceneContainer); // scene对象
this.controls.push(ctr);
}
public removeControl(ctr: IControl): this {

View File

@ -1,4 +1,4 @@
import { IMapService } from '../map/IMapService';
import { Container } from 'inversify';
export enum PositionType {
'TOPRIGHT' = 'topright',
'TOPLEFT' = 'topleft',
@ -16,8 +16,8 @@ export interface IControlCorners {
}
export interface IControl {
setPosition(pos: PositionType): void;
addTo(map: IMapService): void;
onAdd(map: IMapService): HTMLElement;
addTo(sceneContainer: Container): void;
onAdd(): HTMLElement;
hide(): void;
show(): void;
remove(): void;
@ -27,7 +27,7 @@ export interface IControlService {
controlCorners: IControlCorners;
controlContainer: HTMLElement;
init(cfg: IControlServiceCfg): void;
addControl(ctr: IControl, map: IMapService): void;
addControl(ctr: IControl, sceneContainer: Container): void;
removeControl(ctr: IControl): void;
destroy(): void;
}

View File

@ -1,14 +1,23 @@
import Ajv from 'ajv';
import { injectable } from 'inversify';
import { IGlobalConfig, IGlobalConfigService } from './IConfigService';
import { injectable, postConstruct } from 'inversify';
import { ILayerConfig } from '../layer/ILayerService';
import { IGlobalConfigService, ISceneConfig } from './IConfigService';
import sceneConfigSchema from './sceneConfigSchema';
const defaultGlobalConfig: Partial<IGlobalConfig> = {
/**
*
*/
const defaultSceneConfig: Partial<ISceneConfig> = {
type: 'amap',
zoom: 5,
center: [107.622, 39.266],
pitch: 0,
// minZoom: 3,
// maxZoom: 18,
};
/**
*
*/
const defaultLayerConfig: Partial<ILayerConfig> = {
colors: [
'rgb(103,0,31)',
'rgb(178,24,43)',
@ -37,6 +46,17 @@ const defaultGlobalConfig: Partial<IGlobalConfig> = {
'vesica',
],
shape3d: ['cylinder', 'triangleColumn', 'hexagonColumn', 'squareColumn'],
minZoom: 0,
maxZoom: 20,
visible: true,
zIndex: 0,
enableMultiPassRenderer: true,
enablePicking: false,
enableHighlight: false,
highlightColor: 'red',
enableTAA: false,
jitterScale: 1,
enableLighting: false,
};
// @see https://github.com/epoberezkin/ajv#options
@ -47,31 +67,65 @@ const ajv = new Ajv({
@injectable()
export default class GlobalConfigService implements IGlobalConfigService {
private config: Partial<IGlobalConfig> = defaultGlobalConfig;
/**
*
*/
private sceneConfigCache: {
[sceneId: string]: Partial<ISceneConfig>;
} = {};
/**
* Layer
*
*/
private sceneConfigValidator: Ajv.ValidateFunction;
/**
*
*/
private layerConfigCache: {
[layerId: string]: Partial<ILayerConfig & ISceneConfig>;
} = {};
/**
* Layer
*/
private layerConfigValidatorCache: {
[layerName: string]: Ajv.ValidateFunction;
} = {};
public getConfig() {
return this.config;
public getSceneConfig(sceneId: string) {
return this.sceneConfigCache[sceneId];
}
public setAndCheckConfig(config: Partial<IGlobalConfig>) {
this.config = {
...this.config,
public setSceneConfig(sceneId: string, config: Partial<ISceneConfig>) {
this.sceneConfigCache[sceneId] = {
...defaultSceneConfig,
...config,
};
// TODO: validate config with JSON schema
// @see https://github.com/webpack/schema-utils
return true;
}
public reset() {
this.config = defaultGlobalConfig;
public validateSceneConfig(data: object) {
return this.validate(this.sceneConfigValidator, data);
}
public getLayerConfig<IChildLayerConfig>(
layerId: string,
): Partial<ILayerConfig & ISceneConfig & IChildLayerConfig> {
// @ts-ignore
return this.layerConfigCache[layerId];
}
public setLayerConfig(
sceneId: string,
layerId: string,
config: Partial<ILayerConfig>,
) {
// @ts-ignore
this.layerConfigCache[layerId] = {
...this.sceneConfigCache[sceneId],
...defaultLayerConfig,
...config,
};
}
public registerLayerConfigSchemaValidator(layerName: string, schema: object) {
@ -81,14 +135,30 @@ export default class GlobalConfigService implements IGlobalConfigService {
}
public validateLayerConfig(layerName: string, data: object) {
const validate = this.layerConfigValidatorCache[layerName];
if (validate) {
const valid = validate(data);
return this.validate(this.layerConfigValidatorCache[layerName], data);
}
public clean() {
this.sceneConfigCache = {};
this.layerConfigCache = {};
}
@postConstruct()
private registerSceneConfigSchemaValidator() {
this.sceneConfigValidator = ajv.compile(sceneConfigSchema);
}
private validate(
validateFunc: Ajv.ValidateFunction | undefined,
data: object,
) {
if (validateFunc) {
const valid = validateFunc(data);
if (!valid) {
return {
valid,
errors: validate.errors,
errorText: ajv.errorsText(validate.errors),
errors: validateFunc.errors,
errorText: ajv.errorsText(validateFunc.errors),
};
}
}

View File

@ -1,21 +1,65 @@
import Ajv from 'ajv';
import { ILayerGlobalConfig } from '../layer/ILayerService';
import { ILayerConfig } from '../layer/ILayerService';
import { IMapConfig } from '../map/IMapService';
import { IRenderConfig } from '../renderer/IRendererService';
export type IGlobalConfig = IMapConfig & IRenderConfig & ILayerGlobalConfig;
export type ISceneConfig = IMapConfig & IRenderConfig;
export interface IGlobalConfigService {
getConfig(): Partial<IGlobalConfig>;
setAndCheckConfig(config: Partial<IGlobalConfig>): boolean;
reset(): void;
registerLayerConfigSchemaValidator(layerName: string, schema: object): void;
validateLayerConfig(
layerName: string,
data: object,
): {
interface IValidateResult {
valid: boolean;
errors: Ajv.ErrorObject[] | null | undefined;
errorText: string | null;
};
}
export interface IGlobalConfigService {
/**
*
* @param sceneId ID
*/
getSceneConfig(sceneId: string): Partial<ISceneConfig>;
setSceneConfig(sceneId: string, config: Partial<ISceneConfig>): void;
/**
*
* @param data
*/
validateSceneConfig(data: object): IValidateResult;
/**
*
* @param layerId ID
*/
getLayerConfig<IChildLayerConfig>(
layerId: string,
): Partial<ILayerConfig & ISceneConfig & IChildLayerConfig>;
/**
*
* @param sceneId ID
* @param layerId ID
* @param config
*/
setLayerConfig(
sceneId: string,
layerId: string,
config: Partial<ILayerConfig>,
): void;
/**
*
* @param layerName
* @param schema
*/
registerLayerConfigSchemaValidator(layerName: string, schema: object): void;
/**
*
* @param data
*/
validateLayerConfig(layerName: string, data: object): IValidateResult;
/**
* Cache
*/
clean(): void;
}

View File

@ -0,0 +1,35 @@
import { MapType } from '../map/IMapService';
/**
* Schema
*/
export default {
properties: {
// 地图容器 ID
id: {
type: 'string',
},
// 地图类型,目前支持高德 & Mapbox
type: {
enum: [MapType.amap, MapType.mapbox],
},
// 地图缩放等级
zoom: {
type: 'number',
minimum: 0,
maximum: 20,
},
// 地图中心点
center: {
item: {
type: 'number',
},
maxItems: 2,
minItems: 2,
},
// 仰角
pitch: {
type: 'number',
},
},
};

View File

@ -23,7 +23,6 @@ export default class InteractionService extends EventEmitter
public init() {
// 注册事件在地图底图上
this.clear();
this.addEventListenerOnMap();
}
@ -55,7 +54,7 @@ export default class InteractionService extends EventEmitter
this.hammertime = hammertime;
// TODO: 根据场景注册事件到 L7 canvas 上
this.logger.info('add event listeners on canvas');
this.logger.debug('add event listeners on canvas');
}
}
@ -67,13 +66,12 @@ export default class InteractionService extends EventEmitter
}
private onHover = ({ x, y }: MouseEvent) => {
const $containter = this.mapService.getMapContainer();
if ($containter) {
const { top, left } = $containter.getBoundingClientRect();
x -= left;
y -= top;
}
this.emit(InteractionEvent.Hover, { x, y });
};
private clear() {
if (this.hammertime) {
this.hammertime.destroy();
}
this.removeEventListenerOnMap();
this.off(InteractionEvent.Hover);
}
}

View File

@ -1,8 +1,15 @@
import { Container } from 'inversify';
import { SyncBailHook, SyncHook } from 'tapable';
import Clock from '../../utils/clock';
import { IGlobalConfigService } from '../config/IConfigService';
import { ISceneConfig } from '../config/IConfigService';
import { IMapService } from '../map/IMapService';
import { IModel, IModelInitializationOptions } from '../renderer/IModel';
import { IMultiPassRenderer } from '../renderer/IMultiPassRenderer';
import {
IMultiPassRenderer,
IPass,
IPostProcessingPass,
} from '../renderer/IMultiPassRenderer';
import { IRendererService } from '../renderer/IRendererService';
import { IUniform } from '../renderer/IUniform';
import { ISource, ISourceCFG } from '../source/ISourceService';
import {
@ -16,16 +23,6 @@ import {
Triangulation,
} from './IStyleAttributeService';
export interface ILayerGlobalConfig {
colors: string[];
size: number;
shape: string;
shape2d: string[];
shape3d: string[];
scales: {
[key: string]: IScale;
};
}
export interface ILayerModelInitializationOptions {
moduleName: string;
vertexShader: string;
@ -52,11 +49,7 @@ export interface ILayer {
id: string; // 一个场景中同一类型 Layer 可能存在多个
name: string; // 代表 Layer 的类型
inited: boolean; // 是否初始化完成
visible: boolean;
zIndex: number;
minZoom: number;
maxZoom: number;
configService: IGlobalConfigService;
plugins: ILayerPlugin[];
hooks: {
init: SyncBailHook<void, boolean | void>;
@ -75,7 +68,9 @@ export interface ILayer {
options?: ISourceCFG;
};
multiPassRenderer: IMultiPassRenderer;
styleAttributeService: IStyleAttributeService;
getLayerConfig(): Partial<ILayerConfig & ISceneConfig>;
getContainer(): Container;
setContainer(container: Container): void;
buildLayerModel(
options: ILayerModelInitializationOptions &
Partial<IModelInitializationOptions>,
@ -100,12 +95,15 @@ export interface ILayer {
render(): ILayer;
destroy(): void;
source(data: any, option?: ISourceCFG): ILayer;
/**
*
* @param plugin
*/
addPlugin(plugin: ILayerPlugin): ILayer;
getSource(): ISource;
setSource(source: ISource): void;
setEncodedData(encodedData: IEncodeFeature[]): void;
getEncodedData(): IEncodeFeature[];
getStyleOptions(): Partial<ILayerInitializationOptions>;
getScaleOptions(): IScaleOptions;
/**
*
@ -128,13 +126,30 @@ export interface ILayer {
* Layer
*/
export interface ILayerPlugin {
apply(layer: ILayer): void;
apply(
layer: ILayer,
services: {
rendererService: IRendererService;
mapService: IMapService;
styleAttributeService: IStyleAttributeService;
postProcessingPassFactory: (name: string) => IPostProcessingPass<unknown>;
normalPassFactory: (name: string) => IPass<unknown>;
},
): void;
}
/**
* Layer
*/
export interface ILayerInitializationOptions {
export interface ILayerConfig {
colors: string[];
size: number;
shape: string;
shape2d: string[];
shape3d: string[];
scales: {
[key: string]: IScale;
};
minZoom: number;
maxZoom: number;
visible: boolean;
@ -162,6 +177,10 @@ export interface ILayerInitializationOptions {
*
*/
jitterScale: number;
/**
*
*/
enableLighting: boolean;
onHover(pickedFeature: IPickedFeature): void;
onClick(pickedFeature: IPickedFeature): void;
}

View File

@ -1,5 +1,5 @@
import { inject, injectable } from 'inversify';
import { container, ILayer } from '../..';
import { ILayer } from '../..';
import { TYPES } from '../../types';
import Clock from '../../utils/clock';
import { IGlobalConfigService } from '../config/IConfigService';
@ -29,10 +29,6 @@ export default class LayerService implements ILayerService {
public initLayers() {
this.layers.forEach((layer) => {
if (!layer.inited) {
// register plugins in every layer
for (const plugin of layer.plugins) {
plugin.apply(layer);
}
layer.init();
}
});
@ -93,16 +89,11 @@ export default class LayerService implements ILayerService {
}
}
private initPlugin(layer: ILayer) {
for (const plugin of layer.plugins) {
plugin.apply(layer);
}
}
private clear() {
this.renderService.clear({
color: [0, 0, 0, 0],
depth: 1,
stencil: 0,
framebuffer: null,
});
}

View File

@ -2,4 +2,5 @@ export interface ILogService {
error(message: string): void;
warn(message: string): void;
info(message: string): void;
debug(message: string): void;
}

View File

@ -2,7 +2,9 @@ import { injectable } from 'inversify';
import { Log } from 'probe.gl';
import { ILogService } from './ILogService';
const Logger = new Log({ id: 'L7' }).enable(false);
const Logger = new Log({ id: 'L7' }).enable();
// 只输出 debug 级别以上的日志信息
Logger.priority = 4;
@injectable()
export default class LogService implements ILogService {
@ -15,6 +17,10 @@ export default class LogService implements ILogService {
}
public info(message: string): void {
Logger.info(message)();
Logger.info(3, message)();
}
public debug(message: string): void {
Logger.probe(4, message)();
}
}

View File

@ -1,3 +1,4 @@
/// <reference types="amap-js-api" />
import { Map } from 'mapbox-gl';
import { IViewport } from '../camera/ICameraService';
export type Point = [number, number];

View File

@ -13,6 +13,78 @@ type stencilOp =
| gl.INCR_WRAP
| gl.DECR_WRAP;
type BlendingFunctionCombined = Partial<{
src:
| gl.ZERO
| gl.ONE
| gl.SRC_COLOR
| gl.ONE_MINUS_SRC_COLOR
| gl.SRC_ALPHA
| gl.ONE_MINUS_SRC_ALPHA
| gl.DST_COLOR
| gl.ONE_MINUS_DST_COLOR
| gl.DST_ALPHA
| gl.ONE_MINUS_DST_ALPHA
| gl.CONSTANT_COLOR
| gl.ONE_MINUS_CONSTANT_COLOR
| gl.CONSTANT_ALPHA
| gl.ONE_MINUS_CONSTANT_ALPHA
| gl.SRC_ALPHA_SATURATE;
dst:
| gl.ZERO
| gl.ONE
| gl.SRC_COLOR
| gl.ONE_MINUS_SRC_COLOR
| gl.SRC_ALPHA
| gl.ONE_MINUS_SRC_ALPHA
| gl.DST_COLOR
| gl.ONE_MINUS_DST_COLOR
| gl.DST_ALPHA
| gl.ONE_MINUS_DST_ALPHA
| gl.CONSTANT_COLOR
| gl.ONE_MINUS_CONSTANT_COLOR
| gl.CONSTANT_ALPHA
| gl.ONE_MINUS_CONSTANT_ALPHA
| gl.SRC_ALPHA_SATURATE;
}>;
type BlendingFunctionSeparate = Partial<{
srcRGB:
| gl.ZERO
| gl.ONE
| gl.SRC_COLOR
| gl.ONE_MINUS_SRC_COLOR
| gl.SRC_ALPHA
| gl.ONE_MINUS_SRC_ALPHA
| gl.DST_COLOR
| gl.ONE_MINUS_DST_COLOR
| gl.DST_ALPHA
| gl.ONE_MINUS_DST_ALPHA
| gl.CONSTANT_COLOR
| gl.ONE_MINUS_CONSTANT_COLOR
| gl.CONSTANT_ALPHA
| gl.ONE_MINUS_CONSTANT_ALPHA
| gl.SRC_ALPHA_SATURATE;
srcAlpha: number;
dstRGB:
| gl.ZERO
| gl.ONE
| gl.SRC_COLOR
| gl.ONE_MINUS_SRC_COLOR
| gl.SRC_ALPHA
| gl.ONE_MINUS_SRC_ALPHA
| gl.DST_COLOR
| gl.ONE_MINUS_DST_COLOR
| gl.DST_ALPHA
| gl.ONE_MINUS_DST_ALPHA
| gl.CONSTANT_COLOR
| gl.ONE_MINUS_CONSTANT_COLOR
| gl.CONSTANT_ALPHA
| gl.ONE_MINUS_CONSTANT_ALPHA
| gl.SRC_ALPHA_SATURATE;
dstAlpha: number;
}>;
export interface IModelInitializationOptions {
/**
* Shader ShaderLib
@ -85,42 +157,7 @@ export interface IModelInitializationOptions {
// gl.enable(gl.BLEND)
enable: boolean;
// gl.blendFunc
func: Partial<{
srcRGB:
| gl.ZERO
| gl.ONE
| gl.SRC_COLOR
| gl.ONE_MINUS_SRC_COLOR
| gl.SRC_ALPHA
| gl.ONE_MINUS_SRC_ALPHA
| gl.DST_COLOR
| gl.ONE_MINUS_DST_COLOR
| gl.DST_ALPHA
| gl.ONE_MINUS_DST_ALPHA
| gl.CONSTANT_COLOR
| gl.ONE_MINUS_CONSTANT_COLOR
| gl.CONSTANT_ALPHA
| gl.ONE_MINUS_CONSTANT_ALPHA
| gl.SRC_ALPHA_SATURATE;
srcAlpha: number;
dstRGB:
| gl.ZERO
| gl.ONE
| gl.SRC_COLOR
| gl.ONE_MINUS_SRC_COLOR
| gl.SRC_ALPHA
| gl.ONE_MINUS_SRC_ALPHA
| gl.DST_COLOR
| gl.ONE_MINUS_DST_COLOR
| gl.DST_ALPHA
| gl.ONE_MINUS_DST_ALPHA
| gl.CONSTANT_COLOR
| gl.ONE_MINUS_CONSTANT_COLOR
| gl.CONSTANT_ALPHA
| gl.ONE_MINUS_CONSTANT_ALPHA
| gl.SRC_ALPHA_SATURATE;
dstAlpha: number;
}>;
func: BlendingFunctionSeparate;
// gl.blendEquation
equation: {
// TODO: EXT_blend_minmax
@ -134,7 +171,7 @@ export interface IModelInitializationOptions {
/**
* stencil
*/
stencil?: {
stencil?: Partial<{
// gl.enable(gl.STENCIL_TEST)
enable: boolean;
// gl.stencilMask
@ -162,7 +199,7 @@ export interface IModelInitializationOptions {
zfail: stencilOp;
zpass: stencilOp;
};
};
}>;
/**
* cull

View File

@ -26,6 +26,7 @@ export interface IPass<InitializationOptions> {
export interface IPostProcessingPass<InitializationOptions>
extends IPass<InitializationOptions> {
setRenderToScreen(renderToScreen: boolean): void;
setName(name: string): void;
isEnabled(): boolean;
setEnabled(enabled: boolean): void;
updateOptions(config: Partial<InitializationOptions>): void;
@ -49,8 +50,12 @@ export interface IPostProcessor {
export interface IMultiPassRenderer {
getPostProcessor(): IPostProcessor;
resize(viewportWidth: number, viewportHeight: number): void;
add<InitializationOptions>(pass: IPass<InitializationOptions>): void;
add<InitializationOptions>(
pass: IPass<InitializationOptions>,
config?: Partial<InitializationOptions>,
): void;
render(): void;
getRenderFlag(): boolean;
setRenderFlag(enabled: boolean): void;
setLayer(layer: ILayer): void;
}

View File

@ -1,4 +1,15 @@
import { IFramebuffer } from './IFramebuffer';
import { ITexture2D } from './ITexture2D';
export type IUniform = number | number[] | boolean | IFramebuffer | ITexture2D;
interface IStruct {
[structPropName: string]: number | number[] | boolean | IStruct | IStruct[];
}
export type IUniform =
| number
| number[]
| ArrayBufferView
| boolean
| IFramebuffer
| ITexture2D
| IStruct;

View File

@ -0,0 +1,52 @@
import { inject, injectable } from 'inversify';
import { IRendererService, IShaderModuleService } from '../../../index';
import { TYPES } from '../../../types';
import { ICameraService } from '../../camera/ICameraService';
import { IInteractionService } from '../../interaction/IInteractionService';
import { ILayer, ILayerService } from '../../layer/ILayerService';
import { IPass, PassType } from '../IMultiPassRenderer';
/**
* Pass
*/
@injectable()
export default class BaseNormalPass<InitializationOptions = {}>
implements IPass<InitializationOptions> {
@inject(TYPES.IShaderModuleService)
protected readonly shaderModuleService: IShaderModuleService;
protected rendererService: IRendererService;
protected cameraService: ICameraService;
protected interactionService: IInteractionService;
protected layerService: ILayerService;
protected config: Partial<InitializationOptions> | undefined;
public getName() {
return '';
}
public getType() {
return PassType.Normal;
}
public init(layer: ILayer, config?: Partial<InitializationOptions>) {
this.config = config;
this.rendererService = layer
.getContainer()
.get<IRendererService>(TYPES.IRendererService);
this.cameraService = layer
.getContainer()
.get<ICameraService>(TYPES.ICameraService);
this.interactionService = layer
.getContainer()
.get<IInteractionService>(TYPES.IInteractionService);
this.layerService = layer
.getContainer()
.get<ILayerService>(TYPES.ILayerService);
}
public render(layer: ILayer) {
//
}
}

View File

@ -1,10 +1,12 @@
import { inject, injectable } from 'inversify';
import { camelCase, isNil, upperFirst } from 'lodash';
import {
gl,
IModel,
IRendererService,
IShaderModuleService,
} from '../../../index';
import quad from '../../../shaders/post-processing/quad.glsl';
import { TYPES } from '../../../types';
import { ILayer } from '../../layer/ILayerService';
import { IPostProcessingPass, PassType } from '../IMultiPassRenderer';
@ -19,13 +21,14 @@ import { IUniform } from '../IUniform';
export default class BasePostProcessingPass<InitializationOptions = {}>
implements IPostProcessingPass<InitializationOptions> {
@inject(TYPES.IShaderModuleService)
protected readonly shaderModule: IShaderModuleService;
protected readonly shaderModuleService: IShaderModuleService;
@inject(TYPES.IRendererService)
protected readonly rendererService: IRendererService;
protected rendererService: IRendererService;
protected config: Partial<InitializationOptions> | undefined;
protected quad: string = quad;
/**
*
*/
@ -41,10 +44,19 @@ export default class BasePostProcessingPass<InitializationOptions = {}>
*/
private model: IModel;
/**
* 便
*/
private name: string;
private optionsToUpdate: Partial<InitializationOptions> = {};
public getName() {
return '';
return this.name;
}
public setName(name: string) {
this.name = name;
}
public getType() {
@ -53,6 +65,9 @@ export default class BasePostProcessingPass<InitializationOptions = {}>
public init(layer: ILayer, config?: Partial<InitializationOptions>) {
this.config = config;
this.rendererService = layer
.getContainer()
.get<IRendererService>(TYPES.IRendererService);
const { createAttribute, createBuffer, createModel } = this.rendererService;
const { vs, fs, uniforms } = this.setupShaders();
@ -80,16 +95,26 @@ export default class BasePostProcessingPass<InitializationOptions = {}>
enable: false,
},
count: 3,
blend: {
// copy pass 需要混合
enable: this.getName() === 'copy',
},
});
}
public render(layer: ILayer) {
const postProcessor = layer.multiPassRenderer.getPostProcessor();
const { useFramebuffer, getViewportSize } = this.rendererService;
const { useFramebuffer, getViewportSize, clear } = this.rendererService;
const { width, height } = getViewportSize();
useFramebuffer(
this.renderToScreen ? null : postProcessor.getWriteFBO(),
() => {
clear({
framebuffer: postProcessor.getWriteFBO(),
color: [0, 0, 0, 0],
depth: 1,
stencil: 0,
});
this.model.draw({
uniforms: {
u_Texture: postProcessor.getReadFBO(),
@ -133,6 +158,19 @@ export default class BasePostProcessingPass<InitializationOptions = {}>
): {
[uniformName: string]: IUniform;
} | void {
throw new Error('Method not implemented.');
const uniforms: {
[key: string]: IUniform;
} = {};
Object.keys(options).forEach((optionName) => {
// @ts-ignore
if (!isNil(options[optionName])) {
uniforms[`u_${upperFirst(camelCase(optionName))}`] =
// @ts-ignore
options[optionName];
}
});
return uniforms;
}
}

View File

@ -1,28 +1,20 @@
import { inject, injectable } from 'inversify';
import { lazyInject } from '../../../index';
import { TYPES } from '../../../types';
import { IPass, PassType } from '../IMultiPassRenderer';
import { IRendererService } from '../IRendererService';
import { injectable } from 'inversify';
import { ILayer } from '../../layer/ILayerService';
import BaseNormalPass from './BaseNormalPass';
/**
* ClearPass
*/
@injectable()
export default class ClearPass<InitializationOptions = {}>
implements IPass<InitializationOptions> {
@lazyInject(TYPES.IRendererService)
protected readonly rendererService: IRendererService;
public getType() {
return PassType.Normal;
}
export default class ClearPass<
InitializationOptions = {}
> extends BaseNormalPass<InitializationOptions> {
public getName() {
return 'clear';
}
public init() {
//
public init(layer: ILayer, config?: Partial<InitializationOptions>) {
super.init(layer, config);
}
public render() {

View File

@ -1,4 +1,5 @@
import { injectable } from 'inversify';
import { inject, injectable } from 'inversify';
import { TYPES } from '../../../types';
import { ILayer } from '../../layer/ILayerService';
import {
IMultiPassRenderer,
@ -7,7 +8,6 @@ import {
IPostProcessor,
PassType,
} from '../IMultiPassRenderer';
import PostProcessor from './PostProcessor';
/**
* ported from Three.js EffectComposer
@ -31,14 +31,15 @@ import PostProcessor from './PostProcessor';
@injectable()
export default class MultiPassRenderer implements IMultiPassRenderer {
private passes: Array<IPass<unknown>> = [];
@inject(TYPES.IPostProcessor)
private postProcessor: IPostProcessor;
private layer: ILayer;
private renderFlag: boolean;
constructor(layer: ILayer) {
public setLayer(layer: ILayer) {
this.layer = layer;
this.postProcessor = new PostProcessor();
}
public setRenderFlag(renderFlag: boolean) {

View File

@ -1,16 +1,12 @@
import { injectable } from 'inversify';
import { lazyInject } from '../../../index';
import { inject, injectable } from 'inversify';
import { TYPES } from '../../../types';
import {
IInteractionService,
InteractionEvent,
} from '../../interaction/IInteractionService';
import { InteractionEvent } from '../../interaction/IInteractionService';
import { ILayer } from '../../layer/ILayerService';
import { ILogService } from '../../log/ILogService';
import { gl } from '../gl';
import { IFramebuffer } from '../IFramebuffer';
import { IPass, PassType } from '../IMultiPassRenderer';
import { IRendererService } from '../IRendererService';
import { PassType } from '../IMultiPassRenderer';
import BaseNormalPass from './BaseNormalPass';
function decodePickingColor(color: Uint8Array): number {
const [i1, i2, i3] = color;
@ -24,15 +20,10 @@ function decodePickingColor(color: Uint8Array): number {
* @see https://github.com/antvis/L7/blob/next/dev-docs/PixelPickingEngine.md
*/
@injectable()
export default class PixelPickingPass<InitializationOptions = {}>
implements IPass<InitializationOptions> {
@lazyInject(TYPES.IRendererService)
protected readonly rendererService: IRendererService;
@lazyInject(TYPES.IInteractionService)
protected readonly interactionService: IInteractionService;
@lazyInject(TYPES.ILogService)
export default class PixelPickingPass<
InitializationOptions = {}
> extends BaseNormalPass<InitializationOptions> {
@inject(TYPES.ILogService)
protected readonly logger: ILogService;
/**
@ -58,7 +49,8 @@ export default class PixelPickingPass<InitializationOptions = {}>
return 'pixelPicking';
}
public init(layer: ILayer) {
public init(layer: ILayer, config?: Partial<InitializationOptions>) {
super.init(layer, config);
this.layer = layer;
const { createTexture2D, createFramebuffer } = this.rendererService;
@ -98,8 +90,6 @@ export default class PixelPickingPass<InitializationOptions = {}>
depth: 1,
});
// this.logger.info(`picking fbo cleared ${width} ${height}`);
/**
* picking pass multipass
* 1. clear ClearPass
@ -129,7 +119,7 @@ export default class PixelPickingPass<InitializationOptions = {}>
useFramebuffer,
} = this.rendererService;
const { width, height } = getViewportSize();
const { enableHighlight } = this.layer.getStyleOptions();
const { enableHighlight } = this.layer.getLayerConfig();
const xInDevicePixel = x * window.devicePixelRatio;
const yInDevicePixel = y * window.devicePixelRatio;
@ -160,15 +150,21 @@ export default class PixelPickingPass<InitializationOptions = {}>
pickedColors[1] !== 0 ||
pickedColors[2] !== 0
) {
this.logger.info('picked');
this.logger.debug('picked');
const pickedFeatureIdx = decodePickingColor(pickedColors);
const rawFeature = this.layer.getSource()?.data?.dataArray[
pickedFeatureIdx
];
if (!rawFeature) {
// this.logger.error(
// '未找到颜色编码解码后的原始 feature请检查 fragment shader 中末尾是否添加了 `gl_FragColor = filterColor(gl_FragColor);`',
// );
} else {
// trigger onHover/Click callback on layer
this.triggerHoverOnLayer({ x, y, feature: rawFeature });
}
}
});
if (enableHighlight) {
@ -185,7 +181,7 @@ export default class PixelPickingPass<InitializationOptions = {}>
y: number;
feature: unknown;
}) {
const { onHover, onClick } = this.layer.getStyleOptions();
const { onHover, onClick } = this.layer.getLayerConfig();
if (onHover) {
onHover({
x,

View File

@ -1,5 +1,4 @@
import { injectable } from 'inversify';
import { lazyInject } from '../../../index';
import { inject, injectable, postConstruct } from 'inversify';
import { TYPES } from '../../../types';
import { ILayer } from '../../layer/ILayerService';
import { gl } from '../gl';
@ -13,34 +12,13 @@ import { IRendererService } from '../IRendererService';
*/
@injectable()
export default class PostProcessor implements IPostProcessor {
@lazyInject(TYPES.IRendererService)
@inject(TYPES.IRendererService)
protected readonly rendererService: IRendererService;
private passes: Array<IPostProcessingPass<unknown>> = [];
private readFBO: IFramebuffer;
private writeFBO: IFramebuffer;
constructor() {
const { createFramebuffer, createTexture2D } = this.rendererService;
this.readFBO = createFramebuffer({
color: createTexture2D({
width: 1,
height: 1,
wrapS: gl.CLAMP_TO_EDGE,
wrapT: gl.CLAMP_TO_EDGE,
}),
});
this.writeFBO = createFramebuffer({
color: createTexture2D({
width: 1,
height: 1,
wrapS: gl.CLAMP_TO_EDGE,
wrapT: gl.CLAMP_TO_EDGE,
}),
});
}
public getReadFBO() {
return this.readFBO;
}
@ -99,6 +77,28 @@ export default class PostProcessor implements IPostProcessor {
return this.passes.find((p) => p.getName() === name);
}
@postConstruct()
private init() {
const { createFramebuffer, createTexture2D } = this.rendererService;
this.readFBO = createFramebuffer({
color: createTexture2D({
width: 1,
height: 1,
wrapS: gl.CLAMP_TO_EDGE,
wrapT: gl.CLAMP_TO_EDGE,
}),
});
this.writeFBO = createFramebuffer({
color: createTexture2D({
width: 1,
height: 1,
wrapS: gl.CLAMP_TO_EDGE,
wrapT: gl.CLAMP_TO_EDGE,
}),
});
}
private isLastEnabledPass(index: number): boolean {
for (let i = index + 1; i < this.passes.length; i++) {
if (this.passes[i].isEnabled()) {

View File

@ -1,19 +1,15 @@
import { injectable } from 'inversify';
import { lazyInject } from '../../../index';
import { TYPES } from '../../../types';
import { ILayer } from '../../layer/ILayerService';
import { IPass, PassType } from '../IMultiPassRenderer';
import { IRendererService } from '../IRendererService';
import { PassType } from '../IMultiPassRenderer';
import BaseNormalPass from './BaseNormalPass';
/**
* RenderPass PostProcessor readFBO
*/
@injectable()
export default class RenderPass<InitializationOptions = {}>
implements IPass<InitializationOptions> {
@lazyInject(TYPES.IRendererService)
protected readonly rendererService: IRendererService;
export default class RenderPass<
InitializationOptions = {}
> extends BaseNormalPass<InitializationOptions> {
public getType() {
return PassType.Normal;
}
@ -22,8 +18,8 @@ export default class RenderPass<InitializationOptions = {}>
return 'render';
}
public init(layer: ILayer) {
//
public init(layer: ILayer, config?: Partial<InitializationOptions>) {
super.init(layer, config);
}
public render(layer: ILayer) {

View File

@ -1,18 +1,16 @@
import { injectable } from 'inversify';
import { lazyInject } from '../../../index';
import { inject, injectable } from 'inversify';
import blendFS from '../../../shaders/post-processing/blend.glsl';
import copyFS from '../../../shaders/post-processing/copy.glsl';
import quadVS from '../../../shaders/post-processing/quad.glsl';
import { TYPES } from '../../../types';
import { ICameraService } from '../../camera/ICameraService';
import { ILayer } from '../../layer/ILayerService';
import { ILogService } from '../../log/ILogService';
import { IShaderModuleService } from '../../shader/IShaderModuleService';
import { gl } from '../gl';
import { IFramebuffer } from '../IFramebuffer';
import { IModel, IModelInitializationOptions } from '../IModel';
import { IPass, PassType } from '../IMultiPassRenderer';
import { IRendererService } from '../IRendererService';
import { PassType } from '../IMultiPassRenderer';
import BaseNormalPass from './BaseNormalPass';
// Generate halton sequence
// https://en.wikipedia.org/wiki/Halton_sequence
@ -38,18 +36,13 @@ let accumulatingId = 1;
* @see https://yuque.antfin-inc.com/yuqi.pyq/fgetpa/ri52hv
*/
@injectable()
export default class TAAPass<InitializationOptions = {}>
implements IPass<InitializationOptions> {
@lazyInject(TYPES.IRendererService)
protected readonly rendererService: IRendererService;
export default class TAAPass<InitializationOptions = {}> extends BaseNormalPass<
InitializationOptions
> {
@inject(TYPES.IShaderModuleService)
protected readonly shaderModuleService: IShaderModuleService;
@lazyInject(TYPES.IShaderModuleService)
protected readonly shaderModule: IShaderModuleService;
@lazyInject(TYPES.ICameraService)
protected readonly cameraService: ICameraService;
@lazyInject(TYPES.ILogService)
@inject(TYPES.ILogService)
protected readonly logger: ILogService;
/**
@ -89,7 +82,9 @@ export default class TAAPass<InitializationOptions = {}>
return 'taa';
}
public init(layer: ILayer) {
public init(layer: ILayer, config?: Partial<InitializationOptions>) {
super.init(layer, config);
const { createFramebuffer, createTexture2D } = this.rendererService;
this.sampleRenderTarget = createFramebuffer({
color: createTexture2D({
@ -197,11 +192,11 @@ export default class TAAPass<InitializationOptions = {}>
}
private doRender(layer: ILayer) {
this.logger.info(`accumulatingId: ${this.accumulatingId}`);
this.logger.debug(`accumulatingId: ${this.accumulatingId}`);
const { clear, getViewportSize, useFramebuffer } = this.rendererService;
const { width, height } = getViewportSize();
const { jitterScale = 1 } = layer.getStyleOptions();
const { jitterScale = 1 } = layer.getLayerConfig();
// 使用 Halton 序列抖动投影矩阵
const offset = this.haltonSequence[this.frame % this.haltonSequence.length];
@ -227,7 +222,7 @@ export default class TAAPass<InitializationOptions = {}>
layer.multiPassRenderer.setRenderFlag(true);
// 混合
const layerStyleOptions = layer.getStyleOptions();
const layerStyleOptions = layer.getLayerConfig();
useFramebuffer(this.outputRenderTarget, () => {
this.blendModel.draw({
uniforms: {
@ -308,12 +303,14 @@ export default class TAAPass<InitializationOptions = {}>
fragmentShader: string,
options?: Partial<IModelInitializationOptions>,
) {
this.shaderModule.registerModule(shaderModuleName, {
this.shaderModuleService.registerModule(shaderModuleName, {
vs: quadVS,
fs: fragmentShader,
});
const { vs, fs, uniforms } = this.shaderModule.getModule(shaderModuleName);
const { vs, fs, uniforms } = this.shaderModuleService.getModule(
shaderModuleName,
);
const { createAttribute, createBuffer, createModel } = this.rendererService;
return createModel({
vs,

View File

@ -1,9 +1,7 @@
import { inject, injectable } from 'inversify';
import { injectable } from 'inversify';
import { isNil } from 'lodash';
import blur from '../../../../shaders/post-processing/blur.glsl';
import quad from '../../../../shaders/post-processing/quad.glsl';
import { TYPES } from '../../../../types';
import { IRendererService } from '../../IRendererService';
import { IUniform } from '../../IUniform';
import BasePostProcessingPass from '../BasePostProcessingPass';
@ -15,20 +13,15 @@ export interface IBlurHPassConfig {
export default class BlurHPass extends BasePostProcessingPass<
IBlurHPassConfig
> {
@inject(TYPES.IRendererService)
protected readonly rendererService: IRendererService;
public getName() {
return 'blurH';
}
protected setupShaders() {
this.shaderModule.registerModule('blur-pass', {
this.shaderModuleService.registerModule('blur-pass', {
vs: quad,
fs: blur,
});
const { vs, fs, uniforms } = this.shaderModule.getModule('blur-pass');
const { vs, fs, uniforms } = this.shaderModuleService.getModule(
'blur-pass',
);
const { width, height } = this.rendererService.getViewportSize();
return {

View File

@ -1,9 +1,7 @@
import { inject, injectable } from 'inversify';
import { injectable } from 'inversify';
import { isNil } from 'lodash';
import blur from '../../../../shaders/post-processing/blur.glsl';
import quad from '../../../../shaders/post-processing/quad.glsl';
import { TYPES } from '../../../../types';
import { IRendererService } from '../../IRendererService';
import { IUniform } from '../../IUniform';
import BasePostProcessingPass from '../BasePostProcessingPass';
@ -15,20 +13,15 @@ export interface IBlurVPassConfig {
export default class BlurVPass extends BasePostProcessingPass<
IBlurVPassConfig
> {
@inject(TYPES.IRendererService)
protected readonly rendererService: IRendererService;
public getName() {
return 'blurV';
}
public setupShaders() {
this.shaderModule.registerModule('blur-pass', {
this.shaderModuleService.registerModule('blur-pass', {
vs: quad,
fs: blur,
});
const { vs, fs, uniforms } = this.shaderModule.getModule('blur-pass');
const { vs, fs, uniforms } = this.shaderModuleService.getModule(
'blur-pass',
);
const { width, height } = this.rendererService.getViewportSize();
return {

View File

@ -1,10 +1,6 @@
import { inject, injectable } from 'inversify';
import { isNil } from 'lodash';
import { injectable } from 'inversify';
import colorHalftone from '../../../../shaders/post-processing/colorhalftone.glsl';
import quad from '../../../../shaders/post-processing/quad.glsl';
import { TYPES } from '../../../../types';
import { IRendererService } from '../../IRendererService';
import { IUniform } from '../../IUniform';
import BasePostProcessingPass from '../BasePostProcessingPass';
export interface IColorHalftonePassConfig {
@ -17,20 +13,13 @@ export interface IColorHalftonePassConfig {
export default class ColorHalftonePass extends BasePostProcessingPass<
IColorHalftonePassConfig
> {
@inject(TYPES.IRendererService)
protected readonly rendererService: IRendererService;
public getName() {
return 'colorHalftone';
}
protected setupShaders() {
this.shaderModule.registerModule('colorhalftone-pass', {
this.shaderModuleService.registerModule('colorhalftone-pass', {
vs: quad,
fs: colorHalftone,
});
const { vs, fs, uniforms } = this.shaderModule.getModule(
const { vs, fs, uniforms } = this.shaderModuleService.getModule(
'colorhalftone-pass',
);
const { width, height } = this.rendererService.getViewportSize();
@ -44,28 +33,4 @@ export default class ColorHalftonePass extends BasePostProcessingPass<
},
};
}
protected convertOptionsToUniforms(
options: Partial<IColorHalftonePassConfig>,
): {
[uniformName: string]: IUniform;
} | void {
const uniforms: {
[key: string]: IUniform;
} = {};
if (!isNil(options.center)) {
uniforms.u_Center = options.center;
}
if (!isNil(options.angle)) {
uniforms.u_Angle = options.angle;
}
if (!isNil(options.size)) {
uniforms.u_Size = options.size;
}
return uniforms;
}
}

View File

@ -1,29 +1,16 @@
import { injectable } from 'inversify';
import copy from '../../../../shaders/post-processing/copy.glsl';
import quad from '../../../../shaders/post-processing/quad.glsl';
import { IUniform } from '../../IUniform';
import BasePostProcessingPass from '../BasePostProcessingPass';
@injectable()
export default class CopyPass extends BasePostProcessingPass {
public getName() {
return 'copy';
}
public setupShaders() {
this.shaderModule.registerModule('copy-pass', {
this.shaderModuleService.registerModule('copy-pass', {
vs: quad,
fs: copy,
});
return this.shaderModule.getModule('copy-pass');
}
protected convertOptionsToUniforms(
options: Partial<{}>,
): {
[uniformName: string]: IUniform;
} | void {
return {};
return this.shaderModuleService.getModule('copy-pass');
}
}

View File

@ -1,10 +1,6 @@
import { inject, injectable } from 'inversify';
import { isNil } from 'lodash';
import { injectable } from 'inversify';
import hexagonalPixelate from '../../../../shaders/post-processing/hexagonalpixelate.glsl';
import quad from '../../../../shaders/post-processing/quad.glsl';
import { TYPES } from '../../../../types';
import { IRendererService } from '../../IRendererService';
import { IUniform } from '../../IUniform';
import BasePostProcessingPass from '../BasePostProcessingPass';
export interface IHexagonalPixelatePassConfig {
@ -16,20 +12,13 @@ export interface IHexagonalPixelatePassConfig {
export default class HexagonalPixelatePass extends BasePostProcessingPass<
IHexagonalPixelatePassConfig
> {
@inject(TYPES.IRendererService)
protected readonly rendererService: IRendererService;
public getName() {
return 'hexagonalPixelate';
}
protected setupShaders() {
this.shaderModule.registerModule('hexagonalpixelate-pass', {
this.shaderModuleService.registerModule('hexagonalpixelate-pass', {
vs: quad,
fs: hexagonalPixelate,
});
const { vs, fs, uniforms } = this.shaderModule.getModule(
const { vs, fs, uniforms } = this.shaderModuleService.getModule(
'hexagonalpixelate-pass',
);
const { width, height } = this.rendererService.getViewportSize();
@ -43,24 +32,4 @@ export default class HexagonalPixelatePass extends BasePostProcessingPass<
},
};
}
protected convertOptionsToUniforms(
options: Partial<IHexagonalPixelatePassConfig>,
): {
[uniformName: string]: IUniform;
} | void {
const uniforms: {
[key: string]: IUniform;
} = {};
if (!isNil(options.center)) {
uniforms.u_Center = options.center;
}
if (!isNil(options.scale)) {
uniforms.u_Scale = options.scale;
}
return uniforms;
}
}

View File

@ -1,10 +1,6 @@
import { inject, injectable } from 'inversify';
import { isNil } from 'lodash';
import { injectable } from 'inversify';
import ink from '../../../../shaders/post-processing/ink.glsl';
import quad from '../../../../shaders/post-processing/quad.glsl';
import { TYPES } from '../../../../types';
import { IRendererService } from '../../IRendererService';
import { IUniform } from '../../IUniform';
import BasePostProcessingPass from '../BasePostProcessingPass';
export interface IInkPassConfig {
@ -13,20 +9,13 @@ export interface IInkPassConfig {
@injectable()
export default class InkPass extends BasePostProcessingPass<IInkPassConfig> {
@inject(TYPES.IRendererService)
protected readonly rendererService: IRendererService;
public getName() {
return 'ink';
}
protected setupShaders() {
this.shaderModule.registerModule('ink-pass', {
this.shaderModuleService.registerModule('ink-pass', {
vs: quad,
fs: ink,
});
const { vs, fs, uniforms } = this.shaderModule.getModule('ink-pass');
const { vs, fs, uniforms } = this.shaderModuleService.getModule('ink-pass');
const { width, height } = this.rendererService.getViewportSize();
return {
@ -38,20 +27,4 @@ export default class InkPass extends BasePostProcessingPass<IInkPassConfig> {
},
};
}
protected convertOptionsToUniforms(
options: Partial<IInkPassConfig>,
): {
[uniformName: string]: IUniform;
} | void {
const uniforms: {
[key: string]: IUniform;
} = {};
if (!isNil(options.strength)) {
uniforms.u_Strength = options.strength;
}
return uniforms;
}
}

View File

@ -1,8 +1,6 @@
import { injectable } from 'inversify';
import { isNil } from 'lodash';
import noise from '../../../../shaders/post-processing/noise.glsl';
import quad from '../../../../shaders/post-processing/quad.glsl';
import { IUniform } from '../../IUniform';
import BasePostProcessingPass from '../BasePostProcessingPass';
export interface INoisePassConfig {
@ -13,32 +11,12 @@ export interface INoisePassConfig {
export default class NoisePass extends BasePostProcessingPass<
INoisePassConfig
> {
public getName() {
return 'noise';
}
public setupShaders() {
this.shaderModule.registerModule('noise-pass', {
this.shaderModuleService.registerModule('noise-pass', {
vs: quad,
fs: noise,
});
return this.shaderModule.getModule('noise-pass');
}
protected convertOptionsToUniforms(
options: Partial<INoisePassConfig>,
): {
[uniformName: string]: IUniform;
} | void {
const uniforms: {
[key: string]: IUniform;
} = {};
if (!isNil(options.amount)) {
uniforms.u_Amount = options.amount;
}
return uniforms;
return this.shaderModuleService.getModule('noise-pass');
}
}

View File

@ -1,8 +1,6 @@
import { injectable } from 'inversify';
import { isNil } from 'lodash';
import quad from '../../../../shaders/post-processing/quad.glsl';
import sepia from '../../../../shaders/post-processing/sepia.glsl';
import { IUniform } from '../../IUniform';
import BasePostProcessingPass from '../BasePostProcessingPass';
export interface ISepiaPassConfig {
@ -13,32 +11,12 @@ export interface ISepiaPassConfig {
export default class SepiaPass extends BasePostProcessingPass<
ISepiaPassConfig
> {
public getName() {
return 'sepia';
}
public setupShaders() {
this.shaderModule.registerModule('sepia-pass', {
this.shaderModuleService.registerModule('sepia-pass', {
vs: quad,
fs: sepia,
});
return this.shaderModule.getModule('sepia-pass');
}
protected convertOptionsToUniforms(
options: Partial<ISepiaPassConfig>,
): {
[uniformName: string]: IUniform;
} | void {
const uniforms: {
[key: string]: IUniform;
} = {};
if (!isNil(options.amount)) {
uniforms.u_Amount = options.amount;
}
return uniforms;
return this.shaderModuleService.getModule('sepia-pass');
}
}

View File

@ -1,13 +1,13 @@
import { EventEmitter } from 'eventemitter3';
import { inject, injectable } from 'inversify';
import { AsyncParallelHook, AsyncSeriesHook } from 'tapable';
import { AsyncParallelHook } from 'tapable';
import { TYPES } from '../../types';
import { createRendererContainer } from '../../utils/dom';
import { IFontService } from '../asset/IFontService';
import { IIconService, IImage } from '../asset/IIconService';
import { IIconService } from '../asset/IIconService';
import { ICameraService, IViewport } from '../camera/ICameraService';
import { IControlService } from '../component/IControlService';
import { IGlobalConfig, IGlobalConfigService } from '../config/IConfigService';
import { IGlobalConfigService, ISceneConfig } from '../config/IConfigService';
import { IInteractionService } from '../interaction/IInteractionService';
import { ILayer, ILayerService } from '../layer/ILayerService';
import { ILogService } from '../log/ILogService';
@ -15,11 +15,14 @@ import { IMapCamera, IMapService } from '../map/IMapService';
import { IRendererService } from '../renderer/IRendererService';
import { IShaderModuleService } from '../shader/IShaderModuleService';
import { ISceneService } from './ISceneService';
/**
* will emit `loaded` `resize` `destroy` event
*/
@injectable()
export default class Scene extends EventEmitter implements ISceneService {
@inject(TYPES.SceneID)
private readonly id: string;
/**
* 使 Service
*/
@ -54,7 +57,7 @@ export default class Scene extends EventEmitter implements ISceneService {
private readonly interactionService: IInteractionService;
@inject(TYPES.IShaderModuleService)
private readonly shaderModule: IShaderModuleService;
private readonly shaderModuleService: IShaderModuleService;
/**
*
@ -62,6 +65,9 @@ export default class Scene extends EventEmitter implements ISceneService {
private inited: boolean = false;
private initPromise: Promise<void>;
// TODO: 改成状态机
private rendering: boolean = false;
/**
* canvas
*/
@ -85,11 +91,21 @@ export default class Scene extends EventEmitter implements ISceneService {
};
}
public init(globalConfig: IGlobalConfig) {
this.initClear();
this.configService.setAndCheckConfig(globalConfig);
public init(sceneConfig: ISceneConfig) {
// 设置场景配置项
this.configService.setSceneConfig(this.id, sceneConfig);
// 校验场景配置项,失败则终止初始化过程
const { valid, errorText } = this.configService.validateSceneConfig(
this.configService.getSceneConfig(this.id),
);
if (!valid) {
this.logger.error(errorText || '');
return;
}
// 初始化 ShaderModule
this.shaderModule.registerBuiltinModules();
this.shaderModuleService.registerBuiltinModules();
// 初始化资源管理 图片
this.iconService.init();
@ -98,7 +114,7 @@ export default class Scene extends EventEmitter implements ISceneService {
this.controlService.init({
container: document.getElementById(
this.configService.getConfig().id || 'map',
this.configService.getSceneConfig(this.id).id || 'map',
) as HTMLElement,
});
@ -118,7 +134,7 @@ export default class Scene extends EventEmitter implements ISceneService {
// 重新绑定非首次相机更新事件
this.map.onCameraChanged(this.handleMapCameraChanged);
this.logger.info('map loaded');
this.logger.debug('map loaded');
});
/**
@ -127,7 +143,7 @@ export default class Scene extends EventEmitter implements ISceneService {
this.hooks.init.tapPromise('initRenderer', async () => {
// 创建底图之上的 container
const $container = createRendererContainer(
this.configService.getConfig().id || '',
this.configService.getSceneConfig(this.id).id || '',
);
this.$container = $container;
if ($container) {
@ -140,28 +156,35 @@ export default class Scene extends EventEmitter implements ISceneService {
// 初始化 container 上的交互
this.interactionService.init();
this.logger.info('renderer loaded');
this.logger.debug(`scene ${this.id} renderer loaded`);
});
// TODOinit worker, fontAtlas...
// 执行异步并行初始化任务
this.initPromise = this.hooks.init.promise(this.configService.getConfig());
this.initPromise = this.hooks.init.promise(
this.configService.getSceneConfig(this.id),
);
}
public addLayer(layer: ILayer) {
this.logger.info(`add layer ${layer.name}`);
this.logger.debug(`add layer ${layer.name} to scene ${this.id}`);
this.layerService.add(layer);
// scene 创建完成自动调用render 方法
this.render();
}
public async render() {
if (this.rendering) {
return;
}
this.rendering = true;
// 首次初始化,或者地图的容器被强制销毁的需要重新初始化
if (!this.inited) {
// 还未初始化完成需要等待
await this.initPromise;
// 初始化 marker 容器 TODO: 可以放到 map 初始化方法中?
// FIXME: 初始化 marker 容器,可以放到 map 初始化方法中?
this.map.addMarkerContainer();
this.logger.info(' render inited');
this.emit('loaded');
@ -171,14 +194,15 @@ export default class Scene extends EventEmitter implements ISceneService {
// 尝试初始化未初始化的图层
this.layerService.initLayers();
this.layerService.renderLayers();
this.logger.info('render');
this.logger.debug(`scene ${this.id} render`);
this.rendering = false;
}
public destroy() {
this.emit('destroy');
this.inited = false;
this.layerService.destroy();
this.configService.reset();
this.interactionService.destroy();
this.controlService.destroy();
this.removeAllListeners();
@ -186,6 +210,7 @@ export default class Scene extends EventEmitter implements ISceneService {
this.map.destroy();
window.removeEventListener('resize', this.handleWindowResized, false);
}
private handleWindowResized = () => {
this.emit('resize');
if (this.$container) {
@ -209,18 +234,9 @@ export default class Scene extends EventEmitter implements ISceneService {
this.render();
}
};
private handleMapCameraChanged = (viewport: IViewport) => {
this.cameraService.update(viewport);
this.render();
};
private initClear() {
this.inited = false;
this.layerService.destroy();
this.configService.reset();
this.interactionService.destroy();
this.controlService.destroy();
this.removeAllListeners();
this.map.destroy();
window.removeEventListener('resize', this.handleWindowResized, false);
}
}

View File

@ -1,4 +1,5 @@
declare module '*.glsl' {
const value: string;
// @ts-ignore
export default value;
}

View File

@ -17,9 +17,15 @@ const TYPES = {
IInteractionService: Symbol.for('IInteractionService'),
IControlService: Symbol.for('IControlService'),
IStyleAttributeService: Symbol.for('IStyleAttributeService'),
ILayer: Symbol.for('ILayer'),
ILayerPlugin: Symbol.for('ILayerPlugin'),
INormalPass: Symbol.for('INormalPass'),
IPostProcessor: Symbol.for('IPostProcessor'),
IPostProcessingPass: Symbol.for('IPostProcessingPass'),
IFactoryPostProcessingPass: Symbol.for('Factory<IPostProcessingPass>'),
IFactoryNormalPass: Symbol.for('Factory<IFactoryNormalPass>'),
IMultiPassRenderer: Symbol.for('IMultiPassRenderer'),
SceneID: Symbol.for('SceneID'),
};
export { TYPES };

View File

@ -1,5 +1,6 @@
const docStyle = window.document.documentElement.style;
type ELType = HTMLElement | SVGElement;
let containerCounter = 0;
export function createRendererContainer(
domId: string | HTMLDivElement,
): HTMLDivElement | null {
@ -17,7 +18,8 @@ export function createRendererContainer(
width: 100%;
pointer-events: none;
`;
$container.id = 'l7_canvaslayer';
$container.id = `l7-scene-${containerCounter++}`;
$container.classList.add('l7-scene');
$wrapper.appendChild($container);
return $container;
}

View File

@ -3,7 +3,10 @@
"compilerOptions": {
"declarationDir": "./es",
"rootDir": "./src",
"baseUrl": "./"
"baseUrl": "./",
"paths": {
"*": ["node_modules", "typings/*"]
}
},
"include": ["./src"]
}

View File

@ -1,13 +1,14 @@
declare module 'probe.gl' {
class Log {
constructor(options: { id: string });
priority: number;
enable(enabled?: boolean): Log;
debug(priority: number, message: string): () => any;
info(priority: number, message: string): () => any;
warn(message: string): () => any;
info(message: string): () => any;
error(message: string): () => any;
probe(priority: number, message: string): () => any;
}
}
/// <reference path="../../../node_modules/eventemitter3/index.d.ts" />

View File

@ -1,8 +1,10 @@
{
"name": "@antv/l7",
"version": "2.0.0-beta.9",
"version": "2.0.0-beta.10",
"description": "A Large-scale WebGL-powered Geospatial Data Visualization",
"main": "dist/l7.js",
"main": "lib/index.js",
"module": "es/index.js",
"types": "es/index.d.ts",
"sideEffects": true,
"files": [
"dist",
@ -20,17 +22,14 @@
},
"author": "antv",
"license": "MIT",
"dependencies": {
"@antv/l7-component": "^2.0.0-beta.10",
"@antv/l7-core": "^2.0.0-beta.10",
"@antv/l7-layers": "^2.0.0-beta.10",
"@antv/l7-scene": "^2.0.0-beta.10"
},
"gitHead": "0563f357f3a07c099bf1ffa9350e6fa3c88353ae",
"publishConfig": {
"access": "public"
},
"dependencies": {
"@l7/core": "0.0.1",
"@l7/scene": "0.0.1",
"@l7/layers": "0.0.1",
"@l7/component": "0.0.1"
},
"peerDependencies": {
"mapbox-gl": "^1.5.0"
}
}

View File

@ -1,8 +1,4 @@
// @ts-ignore
export * from '@l7/core';
// @ts-ignore
export * from '@l7/scene';
// @ts-ignore
export * from '@l7/layers';
// @ts-ignore
export * from '@l7/component';
export * from '@antv/l7-core';
export * from '@antv/l7-scene';
export * from '@antv/l7-layers';
export * from '@antv/l7-component';

View File

@ -0,0 +1,9 @@
{
"extends": "../../tsconfig.build.json",
"compilerOptions": {
"declarationDir": "./es",
"rootDir": "./src",
"baseUrl": "./"
},
"include": ["./src"]
}

39
packages/layers/README.md Normal file
View File

@ -0,0 +1,39 @@
# L7's Collection of Layers
## Installation
```bash
yarn add @antv/l7-layers
```
## Getting Started
Use built-in layers directly:
```typescript
import { PointLayer } from '@antv/l7-layers';
const layer = new PointLayer({
// ...initialization options
});
```
Create a custom layer with the help of `BaseLayer`:
```typescript
import { BaseLayer } from '@antv/l7-layers';
class MyCustomLayer extends BaseLayer {
// ...override methods
}
const layer = new MyCustomLayer({
// ...initialization options
});
```
## Current Built-in Layers
* PointLayer
* PolygonLayer
* LineLayer
* HeatmapLayer
* RasterLayer

View File

@ -1,7 +1,7 @@
{
"name": "@l7/layers",
"version": "0.0.1",
"description": "",
"name": "@antv/l7-layers",
"version": "2.0.0-beta.10",
"description": "L7's collection of built-in layers",
"main": "lib/index.js",
"module": "es/index.js",
"types": "es/index.d.ts",
@ -22,9 +22,9 @@
"author": "xiaoiver",
"license": "ISC",
"dependencies": {
"@l7/core": "^0.0.1",
"@l7/source": "^0.0.1",
"@l7/utils": "^0.0.1",
"@antv/l7-core": "^2.0.0-beta.10",
"@antv/l7-source": "^2.0.0-beta.10",
"@antv/l7-utils": "^2.0.0-beta.10",
"@mapbox/martini": "^0.1.0",
"@turf/meta": "^6.0.2",
"@types/d3-color": "^1.2.2",

View File

@ -1,38 +1,40 @@
import {
gl,
IAnimateOption,
ICameraService,
IEncodeFeature,
IFontService,
IGlobalConfigService,
IIconService,
IInteractionService,
ILayer,
ILayerInitializationOptions,
ILayerConfig,
ILayerModel,
ILayerModelInitializationOptions,
ILayerPlugin,
ILayerService,
ILogService,
IMapService,
IModel,
IModelInitializationOptions,
IMultiPassRenderer,
IPass,
IPostProcessingPass,
IRendererService,
IScale,
IScaleOptions,
IShaderModuleService,
ISourceCFG,
IStyleAttributeInitializationOptions,
IStyleAttributeService,
IStyleAttributeUpdateOptions,
lazyInject,
lazyMultiInject,
StyleAttributeField,
StyleAttributeOption,
Triangulation,
TYPES,
} from '@l7/core';
import Source from '@l7/source';
} from '@antv/l7-core';
import Source from '@antv/l7-source';
import { EventEmitter } from 'eventemitter3';
import { Container } from 'inversify';
import { isFunction, isObject } from 'lodash';
// @ts-ignore
import mergeJsonSchemas from 'merge-json-schemas';
@ -45,22 +47,6 @@ import baseLayerSchema from './schema';
*/
let layerIdCounter = 0;
/**
* Layer
*/
const defaultLayerInitializationOptions: Partial<ILayerInitializationOptions> = {
minZoom: 0,
maxZoom: 20,
visible: true,
zIndex: 0,
enableMultiPassRenderer: false,
enablePicking: false,
enableHighlight: false,
highlightColor: 'red',
enableTAA: false,
jitterScale: 1,
};
export default class BaseLayer<ChildLayerStyleOptions = {}> extends EventEmitter
implements ILayer {
public id: string = `${layerIdCounter++}`;
@ -91,7 +77,6 @@ export default class BaseLayer<ChildLayerStyleOptions = {}> extends EventEmitter
public multiPassRenderer: IMultiPassRenderer;
// 注入插件集
@lazyMultiInject(TYPES.ILayerPlugin)
public plugins: ILayerPlugin[];
public sourceOption: {
@ -99,72 +84,95 @@ export default class BaseLayer<ChildLayerStyleOptions = {}> extends EventEmitter
options?: ISourceCFG;
};
@lazyInject(TYPES.IStyleAttributeService)
public styleAttributeService: IStyleAttributeService;
@lazyInject(TYPES.ILogService)
protected readonly logger: ILogService;
@lazyInject(TYPES.IGlobalConfigService)
public readonly configService: IGlobalConfigService;
@lazyInject(TYPES.IIconService)
public readonly iconService: IIconService;
@lazyInject(TYPES.IFontService)
public readonly fontService: IFontService;
@lazyInject(TYPES.IRendererService)
public readonly rendererService: IRendererService;
protected layerSource: Source;
protected readonly configService: IGlobalConfigService;
@lazyInject(TYPES.IShaderModuleService)
protected readonly shaderModuleService: IShaderModuleService;
@lazyInject(TYPES.IMapService)
protected readonly map: IMapService;
protected iconService: IIconService;
@lazyInject(TYPES.ILayerService)
protected readonly layerService: ILayerService;
protected fontService: IFontService;
protected rendererService: IRendererService;
protected layerService: ILayerService;
protected interactionService: IInteractionService;
protected mapService: IMapService;
protected styleAttributeService: IStyleAttributeService;
protected layerSource: Source;
protected postProcessingPassFactory: (
name: string,
) => IPostProcessingPass<unknown>;
protected normalPassFactory: (name: string) => IPass<unknown>;
protected layerModel: ILayerModel;
protected enodeOptions: {
[type: string]: {
field: StyleAttributeField;
values?: StyleAttributeOption;
};
} = {};
protected animateOptions: IAnimateOption = { enable: false };
/**
*
*/
private container: Container;
private encodedData: IEncodeFeature[];
private configSchema: object;
private rawConfig: Partial<ILayerConfig & ChildLayerStyleOptions>;
/**
*
*
*/
private styleOptions: Partial<
ILayerInitializationOptions & ChildLayerStyleOptions
>;
private pendingStyleAttributes: Array<{
attributeName: string;
attributeField: StyleAttributeField;
attributeValues?: StyleAttributeOption;
defaultName?: string;
updateOptions?: Partial<IStyleAttributeUpdateOptions>;
}> = [];
private scaleOptions: IScaleOptions = {};
@lazyInject(TYPES.IInteractionService)
private readonly interactionService: IInteractionService;
constructor(
styleOptions: Partial<ILayerInitializationOptions & ChildLayerStyleOptions>,
) {
constructor(config: Partial<ILayerConfig & ChildLayerStyleOptions>) {
super();
this.styleOptions = {
...defaultLayerInitializationOptions,
...styleOptions,
};
const { minZoom, maxZoom, zIndex, visible } = this
.styleOptions as ILayerInitializationOptions;
this.visible = visible;
this.zIndex = zIndex;
this.minZoom = minZoom;
this.maxZoom = maxZoom;
this.rawConfig = config;
}
public getLayerConfig() {
return this.configService.getLayerConfig<ChildLayerStyleOptions>(this.id);
}
public updateLayerConfig(
configToUpdate: Partial<ILayerConfig | ChildLayerStyleOptions>,
) {
const sceneId = this.container.get<string>(TYPES.SceneID);
this.configService.setLayerConfig(sceneId, this.id, {
...this.configService.getLayerConfig(this.id),
...configToUpdate,
});
}
/**
*
* RootContainer 1
* -> SceneContainer 1.*
* -> LayerContainer 1.*
*/
public setContainer(container: Container) {
this.container = container;
}
public getContainer() {
return this.container;
}
public addPlugin(plugin: ILayerPlugin) {
@ -178,6 +186,81 @@ export default class BaseLayer<ChildLayerStyleOptions = {}> extends EventEmitter
}
public init() {
// 设置配置项
const sceneId = this.container.get<string>(TYPES.SceneID);
this.configService.setLayerConfig(sceneId, this.id, this.rawConfig);
// 全局容器服务
this.iconService = this.container.get<IIconService>(TYPES.IIconService);
this.fontService = this.container.get<IFontService>(TYPES.IFontService);
// 场景容器服务
this.rendererService = this.container.get<IRendererService>(
TYPES.IRendererService,
);
this.layerService = this.container.get<ILayerService>(TYPES.ILayerService);
this.interactionService = this.container.get<IInteractionService>(
TYPES.IInteractionService,
);
this.mapService = this.container.get<IMapService>(TYPES.IMapService);
this.postProcessingPassFactory = this.container.get(
TYPES.IFactoryPostProcessingPass,
);
this.normalPassFactory = this.container.get(TYPES.IFactoryNormalPass);
// 图层容器服务
this.styleAttributeService = this.container.get<IStyleAttributeService>(
TYPES.IStyleAttributeService,
);
this.multiPassRenderer = this.container.get<IMultiPassRenderer>(
TYPES.IMultiPassRenderer,
);
this.multiPassRenderer.setLayer(this);
// 完成样式服务注册完成前添加的属性
this.pendingStyleAttributes.forEach(
({
attributeName,
attributeField,
attributeValues,
defaultName,
updateOptions,
}) => {
this.styleAttributeService.updateStyleAttribute(
attributeName,
{
// @ts-ignore
scale: {
field: attributeField,
...this.splitValuesAndCallbackInAttribute(
// @ts-ignore
attributeValues,
// @ts-ignore
this.getLayerConfig()[defaultName || attributeName],
),
},
},
// @ts-ignore
updateOptions,
);
},
);
this.pendingStyleAttributes = [];
// 获取插件集
this.plugins = this.container.getAll<ILayerPlugin>(TYPES.ILayerPlugin);
// 完成插件注册,传入场景和图层容器内的服务
for (const plugin of this.plugins) {
plugin.apply(this, {
rendererService: this.rendererService,
mapService: this.mapService,
styleAttributeService: this.styleAttributeService,
normalPassFactory: this.normalPassFactory,
postProcessingPassFactory: this.postProcessingPassFactory,
});
}
// 触发 init 生命周期插件
this.hooks.init.call();
this.buildModels();
@ -190,22 +273,14 @@ export default class BaseLayer<ChildLayerStyleOptions = {}> extends EventEmitter
values?: StyleAttributeOption,
updateOptions?: Partial<IStyleAttributeUpdateOptions>,
) {
this.styleAttributeService.updateStyleAttribute(
'color',
{
// @ts-ignore
scale: {
field,
...this.splitValuesAndCallbackInAttribute(
// @ts-ignore
values,
this.configService.getConfig().colors,
),
},
},
// @ts-ignore
// 设置 color、size、shape、style 时由于场景服务尚未完成(并没有调用 scene.addLayer因此暂时加入待更新属性列表
this.pendingStyleAttributes.push({
attributeName: 'color',
attributeField: field,
attributeValues: values,
defaultName: 'colors',
updateOptions,
);
});
return this;
}
@ -214,22 +289,12 @@ export default class BaseLayer<ChildLayerStyleOptions = {}> extends EventEmitter
values?: StyleAttributeOption,
updateOptions?: Partial<IStyleAttributeUpdateOptions>,
) {
this.styleAttributeService.updateStyleAttribute(
'size',
{
// @ts-ignore
scale: {
field,
...this.splitValuesAndCallbackInAttribute(
// @ts-ignore
values,
this.configService.getConfig().size,
),
},
},
// @ts-ignore
this.pendingStyleAttributes.push({
attributeName: 'size',
attributeField: field,
attributeValues: values,
updateOptions,
);
});
return this;
}
@ -238,26 +303,12 @@ export default class BaseLayer<ChildLayerStyleOptions = {}> extends EventEmitter
values?: StyleAttributeOption,
updateOptions?: Partial<IStyleAttributeUpdateOptions>,
) {
this.enodeOptions.shape = {
field,
values,
};
this.styleAttributeService.updateStyleAttribute(
'shape',
{
// @ts-ignore
scale: {
field,
...this.splitValuesAndCallbackInAttribute(
// @ts-ignore
values,
this.configService.getConfig().shape,
),
},
},
// @ts-ignore
this.pendingStyleAttributes.push({
attributeName: 'shape',
attributeField: field,
attributeValues: values,
updateOptions,
);
});
return this;
}
public label(
@ -265,22 +316,12 @@ export default class BaseLayer<ChildLayerStyleOptions = {}> extends EventEmitter
values?: StyleAttributeOption,
updateOptions?: Partial<IStyleAttributeUpdateOptions>,
) {
this.styleAttributeService.updateStyleAttribute(
'label',
{
// @ts-ignore
scale: {
field,
...this.splitValuesAndCallbackInAttribute(
// @ts-ignore
values,
null,
),
},
},
// @ts-ignore
this.pendingStyleAttributes.push({
attributeName: 'label',
attributeField: field,
attributeValues: values,
updateOptions,
);
});
return this;
}
public animate(options: IAnimateOption) {
@ -295,7 +336,7 @@ export default class BaseLayer<ChildLayerStyleOptions = {}> extends EventEmitter
};
return this;
}
public style(options: object & Partial<ILayerInitializationOptions>): ILayer {
public style(options: object & Partial<ILayerConfig>): ILayer {
const { passes, ...rest } = options;
// passes 特殊处理
@ -312,10 +353,14 @@ export default class BaseLayer<ChildLayerStyleOptions = {}> extends EventEmitter
);
}
this.styleOptions = {
...this.styleOptions,
this.rawConfig = {
...this.rawConfig,
...rest,
};
if (this.container) {
this.updateLayerConfig(this.rawConfig);
}
return this;
}
public scale(field: string | IScaleOptions, cfg: IScale) {
@ -339,13 +384,17 @@ export default class BaseLayer<ChildLayerStyleOptions = {}> extends EventEmitter
}
public show(): ILayer {
this.visible = true;
this.updateLayerConfig({
visible: true,
});
this.layerService.renderLayers();
return this;
}
public hide(): ILayer {
this.visible = false;
this.updateLayerConfig({
visible: false,
});
this.layerService.renderLayers();
return this;
}
@ -357,17 +406,27 @@ export default class BaseLayer<ChildLayerStyleOptions = {}> extends EventEmitter
}
public isVisible(): boolean {
const zoom = this.map.getZoom();
return this.visible && zoom >= this.minZoom && zoom <= this.maxZoom;
const zoom = this.mapService.getZoom();
const {
visible,
minZoom = -Infinity,
maxZoom = Infinity,
} = this.getLayerConfig();
return !!visible && zoom >= minZoom && zoom <= maxZoom;
}
public setMinZoom(min: number): ILayer {
this.minZoom = min;
public setMinZoom(minZoom: number): ILayer {
this.updateLayerConfig({
minZoom,
});
return this;
}
public setMaxZoom(max: number): ILayer {
this.maxZoom = max;
public setMaxZoom(maxZoom: number): ILayer {
this.updateLayerConfig({
maxZoom,
});
return this;
}
/**
@ -376,7 +435,7 @@ export default class BaseLayer<ChildLayerStyleOptions = {}> extends EventEmitter
public fitBounds(): void {
const source = this.getSource();
const extent = source.extent;
this.map.fitBounds([
this.mapService.fitBounds([
[extent[0], extent[1]],
[extent[2], extent[3]],
]);
@ -391,6 +450,9 @@ export default class BaseLayer<ChildLayerStyleOptions = {}> extends EventEmitter
this.models.forEach((model) => model.destroy());
this.hooks.afterDestroy.call();
// 解绑图层容器中的服务
// this.container.unbind(TYPES.IStyleAttributeService);
}
public isDirty() {
@ -411,9 +473,6 @@ export default class BaseLayer<ChildLayerStyleOptions = {}> extends EventEmitter
return this.layerSource;
}
public getStyleOptions() {
return this.styleOptions;
}
public getScaleOptions() {
return this.scaleOptions;
}
@ -458,7 +517,7 @@ export default class BaseLayer<ChildLayerStyleOptions = {}> extends EventEmitter
});
const { vs, fs, uniforms } = this.shaderModuleService.getModule(moduleName);
const { createModel } = this.rendererService;
const parserData = this.getSource().data.dataArray;
const {
attributes,
elements,

View File

@ -1,15 +1,10 @@
import {
AttributeType,
gl,
ICameraService,
IEncodeFeature,
IFontService,
IGlobalConfigService,
IIconService,
ILayer,
ILayerModel,
ILayerPlugin,
ILogService,
IMapService,
IModel,
IModelUniform,
@ -18,11 +13,9 @@ import {
IStyleAttributeService,
lazyInject,
TYPES,
} from '@l7/core';
} from '@antv/l7-core';
export default class BaseModel implements ILayerModel {
@lazyInject(TYPES.IStyleAttributeService)
public styleAttributeService: IStyleAttributeService;
protected layer: ILayer;
@lazyInject(TYPES.IGlobalConfigService)
@ -34,20 +27,26 @@ export default class BaseModel implements ILayerModel {
@lazyInject(TYPES.IFontService)
protected readonly fontService: IFontService;
@lazyInject(TYPES.IRendererService)
protected readonly rendererService: IRendererService;
@lazyInject(TYPES.IShaderModuleService)
protected readonly shaderModuleService: IShaderModuleService;
@lazyInject(TYPES.IMapService)
protected readonly map: IMapService;
@lazyInject(TYPES.ICameraService)
protected readonly camera: ICameraService;
protected rendererService: IRendererService;
protected styleAttributeService: IStyleAttributeService;
protected mapService: IMapService;
protected cameraService: ICameraService;
constructor(layer: ILayer) {
this.layer = layer;
this.rendererService = layer
.getContainer()
.get<IRendererService>(TYPES.IRendererService);
this.styleAttributeService = layer
.getContainer()
.get<IStyleAttributeService>(TYPES.IStyleAttributeService);
this.mapService = layer.getContainer().get<IMapService>(TYPES.IMapService);
this.cameraService = layer
.getContainer()
.get<ICameraService>(TYPES.ICameraService);
this.registerBuiltinAttributes();
}

View File

@ -1,4 +1,4 @@
import { lngLatToMeters } from '@l7/utils';
import { lngLatToMeters } from '@antv/l7-utils';
import earcut from 'earcut';
import { vec3 } from 'gl-matrix';
import { IPath } from './Path';

View File

@ -1,5 +1,5 @@
import { IEncodeFeature } from '@l7/core';
import { aProjectFlat, lngLatToMeters } from '@l7/utils';
import { IEncodeFeature } from '@antv/l7-core';
import { aProjectFlat, lngLatToMeters } from '@antv/l7-utils';
import earcut from 'earcut';
import { vec3 } from 'gl-matrix';
import getNormals from '../utils/polylineNormal';

5
packages/layers/src/glsl.d.ts vendored Normal file
View File

@ -0,0 +1,5 @@
declare module '*.glsl' {
const content: string;
// @ts-ignore
export default content;
}

View File

@ -1,4 +1,4 @@
import { AttributeType, gl, IEncodeFeature, ILayer } from '@l7/core';
import { AttributeType, gl, IEncodeFeature, ILayer } from '@antv/l7-core';
import BaseLayer from '../core/BaseLayer';
import HeatMapModels, { HeatMapModelType } from './models';
interface IPointLayerStyleOptions {
@ -41,7 +41,7 @@ export default class HeatMapLayer extends BaseLayer<IPointLayerStyleOptions> {
const shapeAttribute = this.styleAttributeService.getLayerStyleAttribute(
'shape',
);
const { shape3d } = this.configService.getConfig();
const { shape3d } = this.getLayerConfig();
const source = this.getSource();
const sourceType = source.data.type;
const shape =

View File

@ -2,12 +2,10 @@ import {
AttributeType,
gl,
IEncodeFeature,
ILayer,
ILayerModel,
IModel,
IModelUniform,
} from '@l7/core';
import BaseModel from '../../core/baseModel';
} from '@antv/l7-core';
import BaseModel from '../../core/BaseModel';
import { HeatmapGridTriangulation } from '../../core/triangulation';
import heatmapGridVert from '../shaders/grid_vert.glsl';
import heatmapGridFrag from '../shaders/hexagon_frag.glsl';
@ -20,7 +18,7 @@ export default class GridModel extends BaseModel {
const {
opacity,
coverage,
} = this.layer.getStyleOptions() as IHeatMapLayerStyleOptions;
} = this.layer.getLayerConfig() as IHeatMapLayerStyleOptions;
return {
u_opacity: opacity || 1.0,
u_coverage: coverage || 0.9,
@ -45,7 +43,7 @@ export default class GridModel extends BaseModel {
}
protected registerBuiltinAttributes() {
// point layer size;
this.layer.styleAttributeService.registerStyleAttribute({
this.styleAttributeService.registerStyleAttribute({
name: 'size',
type: AttributeType.Attribute,
descriptor: {
@ -70,7 +68,7 @@ export default class GridModel extends BaseModel {
});
// point layer size;
this.layer.styleAttributeService.registerStyleAttribute({
this.styleAttributeService.registerStyleAttribute({
name: 'pos', // 顶点经纬度位置
type: AttributeType.Attribute,
descriptor: {

View File

@ -2,12 +2,10 @@ import {
AttributeType,
gl,
IEncodeFeature,
ILayer,
ILayerModel,
IModel,
IModelUniform,
} from '@l7/core';
import BaseModel from '../../core/baseModel';
} from '@antv/l7-core';
import BaseModel from '../../core/BaseModel';
import { PointExtrudeTriangulation } from '../../core/triangulation';
import heatmapGrid3dVert from '../shaders/hexagon_3d_vert.glsl';
import heatmapGridFrag from '../shaders/hexagon_frag.glsl';
@ -21,7 +19,7 @@ export default class Grid3DModel extends BaseModel {
const {
opacity,
coverage,
} = this.layer.getStyleOptions() as IHeatMapLayerStyleOptions;
} = this.layer.getLayerConfig() as IHeatMapLayerStyleOptions;
return {
u_opacity: opacity || 1.0,
u_coverage: coverage || 1.0,
@ -45,7 +43,7 @@ export default class Grid3DModel extends BaseModel {
}
protected registerBuiltinAttributes() {
// point layer size;
this.layer.styleAttributeService.registerStyleAttribute({
this.styleAttributeService.registerStyleAttribute({
name: 'size',
type: AttributeType.Attribute,
descriptor: {
@ -70,7 +68,7 @@ export default class Grid3DModel extends BaseModel {
});
// point layer size;
this.layer.styleAttributeService.registerStyleAttribute({
this.styleAttributeService.registerStyleAttribute({
name: 'normal',
type: AttributeType.Attribute,
descriptor: {
@ -93,7 +91,7 @@ export default class Grid3DModel extends BaseModel {
},
},
});
this.layer.styleAttributeService.registerStyleAttribute({
this.styleAttributeService.registerStyleAttribute({
name: 'pos', // 顶点经纬度位置
type: AttributeType.Attribute,
descriptor: {

View File

@ -1,19 +1,14 @@
import {
AttributeType,
gl,
ICameraService,
IEncodeFeature,
IFramebuffer,
ILayer,
ILayerModel,
IModel,
IModelUniform,
ITexture2D,
lazyInject,
TYPES,
} from '@l7/core';
} from '@antv/l7-core';
import { mat4 } from 'gl-matrix';
import BaseModel from '../../core/baseModel';
import BaseModel from '../../core/BaseModel';
import { HeatmapTriangulation } from '../../core/triangulation';
import { generateColorRamp, IColorRamp } from '../../utils/color';
import heatmap3DFrag from '../shaders/heatmap_3d_frag.glsl';
@ -64,7 +59,7 @@ export default class HeatMapModel extends BaseModel {
createTexture2D,
useFramebuffer,
} = this.rendererService;
const shapeAttr = this.layer.styleAttributeService.getLayerStyleAttribute(
const shapeAttr = this.styleAttributeService.getLayerStyleAttribute(
'shape',
);
const shapeType = shapeAttr?.scale?.field || 'heatmap';
@ -79,7 +74,7 @@ export default class HeatMapModel extends BaseModel {
const {
rampColors,
} = this.layer.getStyleOptions() as IHeatMapLayerStyleOptions;
} = this.layer.getLayerConfig() as IHeatMapLayerStyleOptions;
const imageData = generateColorRamp(rampColors as IColorRamp);
const { width, height } = getViewportSize();
@ -111,7 +106,7 @@ export default class HeatMapModel extends BaseModel {
return [this.intensityModel, this.colorModel];
}
protected registerBuiltinAttributes() {
this.layer.styleAttributeService.registerStyleAttribute({
this.styleAttributeService.registerStyleAttribute({
name: 'dir',
type: AttributeType.Attribute,
descriptor: {
@ -135,7 +130,7 @@ export default class HeatMapModel extends BaseModel {
});
// point layer size;
this.layer.styleAttributeService.registerStyleAttribute({
this.styleAttributeService.registerStyleAttribute({
name: 'size',
type: AttributeType.Attribute,
descriptor: {
@ -243,7 +238,7 @@ export default class HeatMapModel extends BaseModel {
opacity,
intensity = 10,
radius = 5,
} = this.layer.getStyleOptions() as IHeatMapLayerStyleOptions;
} = this.layer.getLayerConfig() as IHeatMapLayerStyleOptions;
this.intensityModel.draw({
uniforms: {
u_opacity: opacity || 1.0,
@ -256,7 +251,7 @@ export default class HeatMapModel extends BaseModel {
private drawColorMode() {
const {
opacity,
} = this.layer.getStyleOptions() as IHeatMapLayerStyleOptions;
} = this.layer.getLayerConfig() as IHeatMapLayerStyleOptions;
this.colorModel.draw({
uniforms: {
u_opacity: opacity || 1.0,
@ -269,11 +264,11 @@ export default class HeatMapModel extends BaseModel {
private draw3DHeatMap() {
const {
opacity,
} = this.layer.getStyleOptions() as IHeatMapLayerStyleOptions;
} = this.layer.getLayerConfig() as IHeatMapLayerStyleOptions;
const invert = mat4.invert(
mat4.create(),
// @ts-ignore
mat4.fromValues(...this.camera.getViewProjectionMatrix()),
mat4.fromValues(...this.cameraService.getViewProjectionMatrix()),
) as mat4;
this.colorModel.draw({
uniforms: {

View File

@ -4,8 +4,8 @@ import {
IEncodeFeature,
IModel,
IModelUniform,
} from '@l7/core';
import BaseModel from '../../core/baseModel';
} from '@antv/l7-core';
import BaseModel from '../../core/BaseModel';
import { HeatmapGridTriangulation } from '../../core/triangulation';
import heatmapGridFrag from '../shaders/hexagon_frag.glsl';
import heatmapGridVert from '../shaders/hexagon_vert.glsl';
@ -20,7 +20,7 @@ export default class HexagonModel extends BaseModel {
const {
opacity,
coverage,
} = this.layer.getStyleOptions() as IHeatMapLayerStyleOptions;
} = this.layer.getLayerConfig() as IHeatMapLayerStyleOptions;
return {
u_opacity: opacity || 1.0,
u_coverage: coverage || 0.9,
@ -45,7 +45,7 @@ export default class HexagonModel extends BaseModel {
}
protected registerBuiltinAttributes() {
// point layer size;
this.layer.styleAttributeService.registerStyleAttribute({
this.styleAttributeService.registerStyleAttribute({
name: 'size',
type: AttributeType.Attribute,
descriptor: {
@ -70,7 +70,7 @@ export default class HexagonModel extends BaseModel {
});
// point layer size;
this.layer.styleAttributeService.registerStyleAttribute({
this.styleAttributeService.registerStyleAttribute({
name: 'pos', // 顶点经纬度位置
type: AttributeType.Attribute,
descriptor: {

View File

@ -13,6 +13,8 @@ varying vec4 v_color;
#pragma include "projection"
#pragma include "project"
#pragma include "picking"
void main() {
v_color = a_Color;
@ -21,4 +23,6 @@ void main() {
// vec2 lnglat = unProjectFlat(a_Pos.xy);
vec4 project_pos = project_position(vec4(a_Pos.xy + offset, 0, 1.0));
gl_Position = project_common_position_to_clipspace(project_pos);
setPickingColor(a_PickingColor);
}

View File

@ -16,14 +16,16 @@ varying vec4 v_color;
#pragma include "projection"
#pragma include "project"
#pragma include "light"
void main() {
#pragma include "picking"
void main() {
mat2 rotationMatrix = mat2(cos(u_angle), sin(u_angle), -sin(u_angle), cos(u_angle));
vec2 offset =(vec2(a_Position.xy * u_radius * rotationMatrix * u_coverage));
vec2 lnglat = unProjectFlat(a_Pos.xy + offset);
vec4 project_pos = project_position(vec4(lnglat, a_Position.z * project_pixel(a_Size), 1.0));
vec4 project_pos = project_position(vec4(lnglat, a_Position.z * a_Size, 1.0));
float lightWeight = calc_lighting(project_pos);
v_color =vec4(a_Color.rgb*lightWeight, a_Color.w);
gl_Position = project_common_position_to_clipspace(project_pos);
setPickingColor(a_PickingColor);
}

Some files were not shown because too many files have changed in this diff Show More