Merge pull request #694 from antvis/shihui_dev

Shihui dev
This commit is contained in:
@thinkinggis 2021-05-31 20:55:07 +08:00 committed by GitHub
commit 1fb2e9d3a2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
154 changed files with 7082 additions and 380 deletions

View File

@ -42,11 +42,11 @@ scene.addLayer(layer);
### minZoom
图层显示最小缩放等级0-18   {number}  Mapbox 0-24 高德 3-18
图层显示最小缩放等级0-18   {number}  Mapbox 0-24 高德 2-19
### maxZoom
图层显示最大缩放等级 0-18   {number}  Mapbox 0-24 高德 3-18
图层显示最大缩放等级 0-18   {number}  Mapbox 0-24 高德 2-19
### autoFit
@ -590,3 +590,17 @@ layer.on('inited', (option) => {});
- target 当前 layer
- type 事件类型
## 图层框选
### boxSelect
参数 option
- box [x1: number, y1: number, x2: number, y2: number] 相较于
- cb (...args: any[]) => void 传入的回调方法,返回框选内部的 feature
```javascript
layer.boxSelect(box, cb);
// (x1, y1), (x2, y2) 框选的方框左上角和右下角相对于地图左上角的像素坐标
// cb 是传入的回调函数,回调函数返回的参数是选中的 feature 对象数组,对象的字段和用户传入的数据相关
```

View File

@ -9,7 +9,7 @@ order: 1
### 数据
绘制弧线只需提供起止点坐标即可
绘制弧线只需提供起止点坐标即可(起止点调换位置,弧线的形状会对称相反,飞线动画的方向也会相反)
```javascript
source(data, {

View File

@ -1,71 +1,5 @@
---
title: LineLayer
order: 2
order: 0
---
`markdown:docs/common/style.md`
## 线图层
### shape
线图层支持 4 种 shape
- line 绘制路径图,
- arc 绘制弧线 通过贝塞尔曲线算法技术弧线
- greatcircle 大圆航线,地图两个点的最近距离不是两个点连线,而是大圆航线
- arc3d 3d 弧线地图 3D 视角
⚠️ 弧线只需要设置起止点坐标即可
```
new LineLayer()
.source(data, {
parser: {
type: 'csv',
x: 'lng1',
y: 'lat1',
x1: 'lng2',
y1: 'lat2',
},
})
```
如果 geojson 数据绘制弧线图 coordinates 第一对坐标为起点,第二对为终点
```
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "LineString",
"coordinates": [
[
106.5234375,
57.51582286553883
],
[
136.40625,
61.77312286453146
]
]
}
}
]
}
```
### size
线图层 可以设置高度
- size 类型为 number 则表示 line 的宽度
- size 类型为 [number , number] 分别表示宽度和高度
```javascript
lineLayer.size(1); // 线的宽度为 1
lineLayer.size([1, 2]); // 宽度为1高度2
```
`markdown:docs/common/layer/base.md`
`markdown:docs/api/layer/line_layer/linelayer.zh.md`

View File

@ -1,5 +1,71 @@
---
title: LineLayer
order: 0
order: 2
---
`markdown:docs/api/layer/line_layer/linelayer.en.md`
`markdown:docs/common/style.md`
## 线图层
### shape
线图层支持 4 种 shape
- line 绘制路径图,
- arc 绘制弧线 通过贝塞尔曲线算法技术弧线
- greatcircle 大圆航线,地图两个点的最近距离不是两个点连线,而是大圆航线
- arc3d 3d 弧线地图 3D 视角
⚠️ 弧线只需要设置起止点坐标即可
```
new LineLayer()
.source(data, {
parser: {
type: 'csv',
x: 'lng1',
y: 'lat1',
x1: 'lng2',
y1: 'lat2',
},
})
```
如果 geojson 数据绘制弧线图 coordinates 第一对坐标为起点,第二对为终点
```
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "LineString",
"coordinates": [
[
106.5234375,
57.51582286553883
],
[
136.40625,
61.77312286453146
]
]
}
}
]
}
```
### size
线图层 可以设置高度
- size 类型为 number 则表示 line 的宽度
- size 类型为 [number , number] 分别表示宽度和高度
```javascript
lineLayer.size(1); // 线的宽度为 1
lineLayer.size([1, 2]); // 宽度为1高度2
```
`markdown:docs/common/layer/base.md`

View File

@ -34,7 +34,7 @@ shape 支持
**图片标注**
通过 `Scene.addImage()` 可以添加图片资源
通过 `Scene.addImage()` 可以添加图片资源
### 代码示例

View File

@ -15,7 +15,8 @@ L7 在内部解决了不同地图底图之间差异,同时 L7 层面统一管
### 引入 Map
```javascript
import { GaodeMap } from '@antv/l7-maps';
import { GaodeMap } from '@antv/l7-maps'; // 默认引入高德2.0
import { GaodeMapV1 } from '@antv/l7-maps'; // 默认引入高德1.x 版本
import { Mapbox } from '@antv/l7-maps';
```
@ -70,7 +71,7 @@ const scene = new Scene({
⚠️ 传入地图实例需要自行引入相关地图的 API
⚠️ viewMode 设置为 3D 模式
⚠️ viewMode 设置为 3D 模式GaodeMap2.0 支持 2D 模式,可以不设置)
#### 传入高德地图实例
@ -92,11 +93,13 @@ const scene = new Scene({
[示例地址](/zh/examples/tutorial/map#amapInstance)
[代码地址](https://github.com/antvis/L7/blob/master/examples/tutorial/map/demo/amapInstance.js)
[示例地址( 2D ](/zh/examples/tutorial/map#amapInstance2d)
[代码地址](https://github.com/antvis/L7/blob/master/examples/tutorial/map/demo/amapInstance.js)
#### 传入 Mapbox 地图实例
```javascript
mapboxgl.accessToken =
'pk.eyJ1IjoibHp4dWUiLCJhIjoiYnhfTURyRSJ9.Ugm314vAKPHBzcPmY1p4KQ';
mapboxgl.accessToken = 'xxxx - token';
const map = new mapboxgl.Map({
container: 'map', // container id
style: 'mapbox://styles/mapbox/streets-v11', // stylesheet location

View File

@ -113,7 +113,7 @@ L7 Logo 的显示位置 默认左下角
<description> _number_ </description>
地图初始显示级别 {number} Mapbox 0-24 高德 3-18
地图初始显示级别 {number} Mapbox 0-24 高德 2-19
### center 地图中心
@ -146,11 +146,11 @@ L7 Logo 的显示位置 默认左下角
### minZoom 最小缩放等级
地图最小缩放等级 {number}  default 0 Mapbox 0-24 高德 3-18
地图最小缩放等级 {number}  default 0 Mapbox 0-24 高德 2-19
### maxZoom 最大缩放等级
地图最大缩放等级 {number}  default 22 Mapbox0-24 高德 3-18
地图最大缩放等级 {number}  default 22 Mapbox0-24 高德 2-19
### rotateEnable 是否允许旋转
@ -166,7 +166,167 @@ L7 Logo 的显示位置 默认左下角
高德地图适用,是否关闭偏移坐标系
## 方法
## Layer 方法
### addLayer(layer) 增加图层对象
增加图层对象
参数 :
- `layer` {ILayer} 图层对象
```javascript
scene.addLayer(layer);
```
### getLayer(id) 获取对应的图层对象
获取对应的图层对象
参数 :
- `id` {string}
```javascript
scene.getLayer('layerID');
```
### getLayers() 获取所有的地图图层
获取所有的地图图层
```javascript
scene.getLayers();
```
### getLayerByName(name) 根据图层名称获取图层
根据图层名称获取图层
参数
- `name` {string} layer 初始化可配置图层 name
```javascript
scene.getLayerByName(name); // return Layer 图层对象
```
### removeLayer 移除 layer 图层
移除 layer 图层
```javascript
scene.removeLayer(layer);
```
参数 :
- `layer` {Layer}
### removeAllLayer() 移除所有的图层对象
移除所有的图层对象
```javascript
scene.removeAllLayer();
```
## 控制组件方法
### addControl(ctl) 添加组件控件
添加组件控件
参数 :
- `crl` { IControl } 用户创建的控件对象
```javascript
scene.addControl(ctl);
```
### removeControl(ctr) 移除用户添加的组件控件
移除用户添加的组件控件
参数 :
- `ctl` { IControl } 用户创建的控件对象
```javascript
scene.removeControl(ctl);
```
### getControlByName(name) 根据控件的名称来获取控件
根据控件的名称来获取控件
- `name` { string }
```javascript
const zoomControl = new Zoom({
// zoom 控件
name: 'z1', // 用户传入的控件名称(也可以不传入,该控件默认名称为 zoom
position: 'topright',
});
scene.getControlByName('z1');
```
## 标记方法
### addMarker(maker) 添加标记
往场景中添加标记对象
参数 :
- `maker` { IMarker } Marker 实例
```javascript
const marker = new Marker({
element: el,
}).setLnglat({ lng: nodes[i].x * 1, lat: nodes[i].y });
scene.addMarker(marker);
```
### addMarkerLayer(layer) 添加 Marker 统一管理图层
当用户需要添加许多个 Marker 实例时,为了方便管理可以使用 markerLayer 对象统一管理
参数 :
- `layer` { IMarkerLayer } 标记图层对象
```javascript
const markerLayer = new MarkerLayer();
scene.addMarkerLayer(markerLayer);
```
[示例地址](/zh/examples/point/marker#markerlayer)
### removeMarkerLayer(layer) 移除标签图层
移除标签图层
参数 :
- `layer` { IMarkerLayer } 标记图层对象
```javascript
scene.removeMarkerLayer(markerLayer);
```
### removeAllMakers() 移除场景中所有的标签对象
移除场景中所有的标签对象
```javascript
scene.removeAllMakers();
```
## 地图方法
### getZoom 获取缩放等级
@ -178,30 +338,6 @@ scene.getZoom();
return {float}   当前缩放等级
### getLayers() 获取所有图层
获取所有的地图图层
```javascript
scene.getLayers();
```
### getLayerByName(name) 根据名称获取图层
根据图层名称获取图层
参数
- name {string}
layer 初始化可配置图层 name
```javascript
scene.getLayerByName(name);
```
return Layer 图层对象
### getCenter() 获取地图中心
获取地图中心点
@ -385,18 +521,6 @@ scene.fitBounds([
]);
```
### removeLayer 移除图层
移除 layer
```javascript
scene.removeLayer(layer);
```
参数
- `layer` {Layer}
### exportMap 导出地图图片
导出地图,目前仅支持导出可视化层,不支持底图导出
@ -411,10 +535,133 @@ scene.exportMap('png');
scene 销毁方法,离开页面,或者不需要使用地图可以调用
```
```javascript
scene.destroy();
```
## iconfont 映射支持
### addIconFont(name, fontUnicode) 增加对数据中 unicode 的映射支持
支持对用户传入的数据进行 unicode 的映射,在内部维护一组名称和对应 key 的键值对
参数 :
- `name` {string}
- `fontUnicode` {string}
```javascript
scene.addIconFont('icon1', '&#xe64b;');
scene.addIconFont('icon2', '&#xe64c;');
scene.addFontFace(fontFamily, fontPath);
const pointIconFontLayer = new PointLayer({})
.source(
[
{
j: 140,
w: 34,
m: 'icon1',
},
{
j: 140,
w: 36,
m: 'icon2',
},
],
{
parser: {
type: 'json',
x: 'j',
y: 'w',
},
},
)
.shape('m', 'text')
.size(12)
.color('w', ['#f00', '#f00', '#0f0'])
.style({
fontFamily,
iconfont: true,
textAllowOverlap: true,
});
scene.addLayer(pointIconFontLayer);
```
### addIconFonts(options) 同时传入多组 name - unicode 的键值对
同时传入多组 name - unicode 的键值对
参数 :
- `options` { Array<[name, unicode]> }
```javascript
scene.addIconFonts([
['icon1', '&#xe64b;'],
['icon2', '&#xe64c;'],
]);
```
## 全局资源
### addImage(id, img) 全局中添加的图片资源
在 L7 的图层对象可以使用在 scene 全局中添加的图片资源
参数 :
- `id` {string}
- `img` {HTMLImageElement | File | string}
```javascript
scene.addImage(
'02',
'https://gw.alipayobjects.com/zos/bmw-prod/ce83fc30-701f-415b-9750-4b146f4b3dd6.svg',
);
```
[示例地址](/zh/examples/gallery/animate#animate_path_texture)
### hasImage(id) 判断全局图片资源
判断是否已经在全局添加过相应的图片资源
参数 :
- `id` {string}
```javascript
scene.hasImage('imageID');
```
### removeImage(id) 全局删除图片资源
从全局删除对应的图片资源
参数 :
- `id` {string}
```javascript
scene.removeImage('imageID');
```
### addFontFace(fontFamily, fontPath) 添加字体文件
添加字体文件
参数 :
- `fontFamily` {string} 用户为自己定义的字体名称
- `fontPath` {string} 导入的文件地址
```javascript
let fontFamily = 'iconfont';
let fontPath =
'//at.alicdn.com/t/font_2534097_iiet9d3nekn.woff2?t=1620444089776';
scene.addFontFace(fontFamily, fontPath);
```
## 事件
### on

View File

@ -7,7 +7,7 @@ order: 0
## 概述
source 地理数据处理模块主要包含数据解析parser),和数据处理(transform);
source 地理数据处理模块主要包含数据解析parser),和数据处理transform
- data
- option
@ -18,7 +18,7 @@ source 地理数据处理模块主要包含数据解析parser),和数据
### parser
不同数据类型处理成统一数据格式。矢量数据包括 GeoJON, CSVJson 等不同数据格式,栅格数据,包括 RasterImage 数据。将来还会支持瓦片格式数据。
不同数据类型处理成统一数据格式。矢量数据包括 GeoJON CSVJson 等不同数据格式,栅格数据,包括 RasterImage 数据。将来还会支持瓦片格式数据。
空间数据分矢量数据和栅格数据两大类

View File

@ -16,11 +16,11 @@
### minZoom
<description> _number_ **可选** _default:_ `0`</description>
图层显示最小缩放等级0-18   {number}  Mapbox 0-24 高德 3-18
图层显示最小缩放等级0-18   {number}  Mapbox 0-24 高德 2-19
### maxZoom
<description> _number_ **可选** _default:_ `22`</description>
图层显示最大缩放等级 0-18   {number}  Mapbox 0-24 高德 3-18
图层显示最大缩放等级 0-18   {number}  Mapbox 0-24 高德 2-19
### autoFit
<description> _bool_ **可选** _default:_ `false`</description>

View File

@ -1,10 +1,10 @@
import { Scene } from '@antv/l7';
import { DrillDownLayer } from '@antv/l7-district';
import { Mapbox } from '@antv/l7-maps';
import { GaodeMap } from '@antv/l7-maps';
const colors = [ '#B8E1FF', '#7DAAFF', '#3D76DD', '#0047A5', '#001D70' ];
const scene = new Scene({
id: 'map',
map: new Mapbox({
map: new GaodeMap({
center: [ 116.2825, 39.9 ],
pitch: 0,
style: 'blank',

View File

@ -9,6 +9,16 @@
"title": "中国地图钻取",
"screenshot": "https://gw.alipayobjects.com/mdn/rms_8e1672/afts/img/A*xjjARqU70xoAAAAAAAAAAABkARQnAQ"
},
{
"filename": "drill_down_view.js",
"title": "中国地图钻取",
"screenshot": "https://gw.alipayobjects.com/mdn/rms_23a451/afts/img/A*KphnSq_0C6sAAAAAAAAAAAAAARQnAQ"
},
{
"filename": "drill_down_province.js",
"title": "省级地图钻取",
"screenshot": "https://gw.alipayobjects.com/mdn/rms_23a451/afts/img/A*fVHISrzrz44AAAAAAAAAAAAAARQnAQ"
},
{
"filename": "drill_down_bubble.js",
"title": "中国地图钻取气泡图",

View File

@ -203,7 +203,7 @@ class Province extends React.Component {
});
const scene = new Scene({
id: 'map',
map: new Mapbox({
map: new GaodeMap({
center: [116.2825, 39.9],
pitch: 0,
style: 'blank',

View File

@ -0,0 +1,40 @@
import { Scene, LineLayer } from '@antv/l7';
import { GaodeMap } from '@antv/l7-maps';
const scene = new Scene({
id: 'map',
map: new GaodeMap({
center: [ 120.19382669582967, 30.258134 ],
pitch: 20,
zoom: 18,
style: 'light'
})
});
scene.on('loaded', () => {
scene.addImage(
'02',
'https://gw.alipayobjects.com/zos/bmw-prod/ce83fc30-701f-415b-9750-4b146f4b3dd6.svg'
);
fetch(
'https://gw.alipayobjects.com/os/basement_prod/40ef2173-df66-4154-a8c0-785e93a5f18e.json'
)
.then(res => res.json())
.then(data => {
const layer = new LineLayer()
.source(data)
.size(4)
.shape('line')
.texture('02')
.color('#25d8b7')
.animate({
interval: 1, // 间隔
duration: 1, // 持续时间,延时
trailLength: 2 // 流线长度
})
.style({
lineTexture: true, // 开启线的贴图功能
iconStep: 100 // 设置贴图纹理的间距
});
scene.addLayer(layer);
});
});

View File

@ -4,7 +4,7 @@ import { GaodeMap } from '@antv/l7-maps';
const scene = new Scene({
id: 'map',
map: new GaodeMap({
style: 'amap://styles/a49ef8d081db7b85adb2e90ba7941f1e?isPublic=true',
style: 'dark',
center: [ 120.173104, 30.244072 ],
pitch: 70.41138037735848,
zoom: 17.18,

View File

@ -4,6 +4,11 @@
"en": "Gallery"
},
"demos": [
{
"filename": "animate_path_texture.js",
"title": "路径贴图",
"screenshot":"https://gw.alipayobjects.com/mdn/rms_23a451/afts/img/A*0UrUTakTFQsAAAAAAAAAAAAAARQnAQ"
},
{
"filename": "animate_path.js",
"title": "路径动画",

View File

@ -4,6 +4,11 @@
"en": "line"
},
"demos": [
{
"filename": "wind.js",
"title": "风场弧线",
"screenshot": "https://gw.alipayobjects.com/mdn/rms_23a451/afts/img/A*LpcBTKiazZcAAAAAAAAAAAAAARQnAQ"
},
{
"filename": "arcCircle.js",
"title": "大圆弧线",

View File

@ -0,0 +1,41 @@
import { Scene, LineLayer } from '@antv/l7';
import { GaodeMap } from '@antv/l7-maps';
const scene = new Scene({
id: 'map',
map: new GaodeMap({
pitch: 0,
style: 'light',
center: [ 60, 40.7128 ],
zoom: 2
})
});
scene.on('loaded', () => {
fetch(
'https://gw.alipayobjects.com/os/bmw-prod/7455fead-1dc0-458d-b91a-fb4cf99e701e.txt'
)
.then(res => res.text())
.then(data => {
const layer = new LineLayer({ blend: 'normal' })
.source(data,
{
parser: {
type: 'csv',
x: 'lng1',
y: 'lat1',
x1: 'lng2',
y1: 'lat2'
}
})
.size(1)
.shape('arc')
.color('#6495ED')
.animate({
duration: 4,
interval: 0.2,
trailLength: 0.6
});
// .forward(false)
scene.addLayer(layer);
});
});

View File

@ -16,14 +16,19 @@
},
{
"filename": "road_light.js",
"title": "路径",
"title": "路径light",
"screenshot":"https://gw.alipayobjects.com/mdn/antv_site/afts/img/A*LuXiTZAq_84AAAAAAAAAAABkARQnAQ"
},
{
"filename": "road_dark.js",
"title": "路径",
"title": "路径dark",
"screenshot":"https://gw.alipayobjects.com/mdn/antv_site/afts/img/A*DDjQRLEnwpoAAAAAAAAAAABkARQnAQ"
},
{
"filename": "road_red.js",
"title": "路径2d",
"screenshot":"https://gw.alipayobjects.com/mdn/rms_23a451/afts/img/A*1cF2RYwkcq8AAAAAAAAAAAAAARQnAQ"
},
{
"filename": "road_dark_dash.js",
"title": "路径虚线",

View File

@ -0,0 +1,33 @@
import {
Scene,
LineLayer
} from '@antv/l7';
import {
GaodeMap
} from '@antv/l7-maps';
const scene = new Scene({
id: 'map',
map: new GaodeMap({
center: [ 116.3956, 39.9392 ],
pitch: 0,
zoom: 10,
rotation: 0,
style: 'amap://styles/light',
viewMode: '2D'
})
});
scene.on('loaded', () => {
fetch(
'https://gw.alipayobjects.com/os/basement_prod/0d2f0113-f48b-4db9-8adc-a3937243d5a3.json'
)
.then(res => res.json())
.then(data => {
const layer = new LineLayer({})
.source(data)
.size(1.5)
.shape('line')
.color('标准名称', [ '#5B8FF9', '#5CCEA1', '#5D7092' ]);
scene.addLayer(layer);
});
});

View File

@ -18,6 +18,11 @@
"filename": "weather.js",
"title": "天气",
"screenshot": "https://gw.alipayobjects.com/mdn/antv_site/afts/img/A*-nk1RZJeGooAAAAAAAAAAABkARQnAQ"
},
{
"filename": "road.js",
"title": "路线图",
"screenshot": "https://gw.alipayobjects.com/mdn/rms_23a451/afts/img/A*2HLeQ4AjxacAAAAAAAAAAAAAARQnAQ"
}
]
}

View File

@ -0,0 +1,106 @@
import { Scene, LineLayer, PointLayer } from '@antv/l7';
import { GaodeMap } from '@antv/l7-maps';
const scene = new Scene({
id: 'map',
map: new GaodeMap({
center: [ 120.115, 30.221 ],
pitch: 40,
zoom: 16,
viewMode: '3D'
})
});
scene.on('loaded', () => {
fetch(
'https://gw.alipayobjects.com/os/bmw-prod/91d27a97-869a-459b-a617-498dcc9c3e7f.json'
)
.then(res => res.json())
.then(data => {
scene.addImage(
'road',
'https://gw.alipayobjects.com/mdn/rms_23a451/afts/img/A*haGlTpW2BQgAAAAAAAAAAAAAARQnAQ'
);
const layer = new LineLayer()
.source(data)
.size(8)
.shape('line')
.texture('road')
.color('rgb(20, 180, 90)')
.animate({
interval: 1, // 间隔
duration: 1, // 持续时间,延时
trailLength: 2 // 流线长度
})
.style({
lineTexture: true, // 开启线的贴图功能
iconStep: 200 // 设置贴图纹理的间距
});
scene.addLayer(layer);
scene.addImage(
'start',
'https://gw.alipayobjects.com/zos/bmw-prod/ebb0af57-4a8a-46e0-a296-2d51f9fa8007.svg'
);
scene.addImage(
'visitor',
'https://gw.alipayobjects.com/zos/bmw-prod/64db255d-b636-4929-b072-068e75178b23.svg'
);
scene.addImage(
'museum',
'https://gw.alipayobjects.com/zos/bmw-prod/0630591d-64db-4057-a04d-d65f43aebf0f.svg'
);
scene.addImage(
'supermarket',
'https://gw.alipayobjects.com/zos/bmw-prod/ab42799d-dea6-4d37-bd62-3ee3e06bf6c0.svg'
);
scene.addImage(
'tower',
'https://gw.alipayobjects.com/zos/bmw-prod/6d27cf89-638c-432b-a8c4-cac289ee98a8.svg'
);
scene.addImage(
'end',
'https://gw.alipayobjects.com/zos/bmw-prod/59717737-5652-479f-9e6b-e7d2c5441446.svg'
);
const imageLayer = new PointLayer()
.source([{
lng: 120.11025885601617,
lat: 30.22006389085372,
icon: 'start'
}, {
lng: 120.11123578376913,
lat: 30.220443561196277,
icon: 'visitor'
}, {
lng: 120.11408457779198,
lat: 30.22019805564678,
icon: 'museum'
}, {
lng: 120.11683172384723,
lat: 30.21875509667716,
icon: 'supermarket'
}, {
lng: 120.11945546294194,
lat: 30.218724022876376,
icon: 'tower'
}, {
lng: 120.1184189041221,
lat: 30.21783201718256,
icon: 'end'
}
], {
parser: {
type: 'json',
x: 'lng',
y: 'lat'
}
})
.shape('icon', [ 'start', 'visitor', 'museum', 'supermarket', 'tower', 'end' ])
.size(35)
.style({
offsets: [ 0, 20 ]
});
scene.addLayer(imageLayer);
});
});

View File

@ -0,0 +1,54 @@
import { Scene, PointLayer } from '@antv/l7';
import { GaodeMap } from '@antv/l7-maps';
const scene = new Scene({
id: 'map',
map: new GaodeMap({
style: 'light',
center: [ 112, 23.69 ],
zoom: 2.5
})
});
fetch(
'https://gw.alipayobjects.com/os/basement_prod/9078fd36-ce8d-4ee2-91bc-605db8315fdf.csv'
)
.then(res => res.text())
.then(data => {
const pointLayer = new PointLayer({})
.source(data, {
parser: {
type: 'csv',
x: 'Longitude',
y: 'Latitude'
}
})
.shape('circle')
.active(true)
.animate(true)
.size(40)
.color('#ffa842')
.style({
opacity: 1,
offsets: [ 40, 40 ]
});
const pointLayer2 = new PointLayer({})
.source(data, {
parser: {
type: 'csv',
x: 'Longitude',
y: 'Latitude'
}
})
.shape('circle')
.active(true)
.animate(true)
.size(50)
.color('#f00')
.style({
opacity: 1
});
scene.addLayer(pointLayer);
scene.addLayer(pointLayer2);
});

View File

@ -0,0 +1,52 @@
import { Scene, PointLayer } from '@antv/l7';
import { GaodeMap } from '@antv/l7-maps';
const scene = new Scene({
id: 'map',
map: new GaodeMap({
style: 'light',
center: [ 120.19382669582967, 30.258134 ],
zoom: 10
})
});
const radius = 0.1;
function pointOnCircle(angle) {
return {
type: 'FeatureCollection',
features: [
{
type: 'Feature',
properties: {},
geometry: {
type: 'Point',
coordinates: [
120.19382669582967 + Math.cos(angle) * radius,
30.258134 + Math.sin(angle) * radius
]
}
}
]
};
}
const layer = new PointLayer({})
.source(pointOnCircle(0))
.shape('circle')
.size(15) // default 1
.active(false)
.color('#2F54EB')
.style({
stroke: '#fff',
strokeWidth: 2,
opacity: 1
});
scene.addLayer(layer);
layer.setData(pointOnCircle(1000));
function animateMarker(timestamp) {
layer.setData(pointOnCircle(timestamp / 1000));
scene.render();
requestAnimationFrame(animateMarker);
}
animateMarker(0);

View File

@ -8,8 +8,16 @@
"filename": "scatter.js",
"title": "散点图",
"screenshot":"https://gw.alipayobjects.com/mdn/antv_site/afts/img/A*LnlmQ7sFWigAAAAAAAAAAABkARQnAQ"
},
{
"filename": "dynamicScatter.js",
"title": "动态散点",
"screenshot":"https://gw.alipayobjects.com/mdn/rms_23a451/afts/img/A*iourTIHnDk0AAAAAAAAAAAAAARQnAQ"
},
{
"filename": "animatePoint.js",
"title": "水波散点",
"screenshot":"https://gw.alipayobjects.com/mdn/rms_23a451/afts/img/A*BsEnTrJ5JRcAAAAAAAAAAAAAARQnAQ"
}
]
}

View File

@ -0,0 +1,47 @@
import { Scene, PointLayer } from '@antv/l7';
import { GaodeMap } from '@antv/l7-maps';
const scene = new Scene({
id: 'map',
map: new GaodeMap({
center: [ 110, 30 ],
pitch: 0,
// style: 'light',
style: 'amap://styles/453e2f8e11603fc8f7548fe18959e9e9',
zoom: 5
})
});
const fontFamily = 'iconfont';
const fontPath = '//at.alicdn.com/t/font_2534097_fcae9o2mxbv.woff2?t=1622200439140';
scene.addFontFace(fontFamily, fontPath);
scene.addIconFont('icon1', '&#xe6d4;');
scene.on('loaded', () => {
fetch(
'https://gw.alipayobjects.com/os/bmw-prod/70408903-80db-4278-a318-461604acb2df.json'
)
.then(res => res.json())
.then(data => {
const pointLayer = new PointLayer({})
.source(data.list, {
parser: {
type: 'json',
x: 'j',
y: 'w'
}
})
.shape('icon', 'text')
.size(20)
.color('w', [ '#a6cee3', '#1f78b4', '#b2df8a', '#33a02c', '#fb9a99' ])
.style({
textAnchor: 'center', // 文本相对锚点的位置 center|left|right|top|bottom|top-left
textOffset: [ 40, 0 ], // 文本相对锚点的偏移量 [水平, 垂直]
padding: [ 0, 0 ], // 文本包围盒 padding [水平,垂直],影响碰撞检测结果,避免相邻文本靠的太近
stroke: '#ffffff', // 描边颜色
fontFamily,
iconfont: true,
textAllowOverlap: true
});
scene.addLayer(pointLayer);
});
});

View File

@ -0,0 +1,268 @@
import { Scene, PointLayer } from '@antv/l7';
import { GaodeMap } from '@antv/l7-maps';
const scene = new Scene({
id: 'map',
map: new GaodeMap({
center: [ 120.5, 30.2 ],
pitch: 0,
style: 'amap://styles/453e2f8e11603fc8f7548fe18959e9e9',
zoom: 8.5,
zooms: [ 8, 10 ],
viewMode: '2D'
})
});
const dataColor = {
bigRainBC: '#285A8C',
middleRainBC: '#326EA0',
smallRainBC: '#4678AA',
sunBC: '#00BFFF',
cloudBC: '#1E90FF'
};
const originData = [
{
lng: 121.7,
lat: 30.6,
iconType: 'hugeRain',
iconColor: '#4678D2',
backgoundColor: dataColor.bigRainBC,
temperature: '20℃',
weather: '大雨'
},
{
lng: 119.2,
lat: 30.0,
iconType: 'smallRain',
iconColor: '#6EA0FF',
backgoundColor: dataColor.smallRainBC,
temperature: '22℃',
weather: '小雨'
},
{
lng: 119.67,
lat: 30.2,
iconType: 'sun',
iconColor: '#FFA500',
backgoundColor: dataColor.sunBC,
temperature: '28℃',
weather: '晴朗'
},
{
lng: 119.63,
lat: 30.6,
iconType: 'sun',
iconColor: '#FFA500',
backgoundColor: dataColor.sunBC,
temperature: '28℃',
weather: '晴朗'
},
{
lng: 120,
lat: 30,
iconType: 'sun',
iconColor: '#FFA500',
backgoundColor: dataColor.sunBC,
temperature: '28℃',
weather: '晴朗'
},
{
lng: 120.2,
lat: 30.5,
iconType: 'sun',
iconColor: '#FFA500',
backgoundColor: dataColor.sunBC,
temperature: '28℃',
weather: '晴朗'
},
{
lng: 121.5,
lat: 31.4,
iconType: 'cloud',
iconColor: '#F0F8FF',
backgoundColor: dataColor.cloudBC,
temperature: '22℃',
weather: '多云'
},
{
lng: 120,
lat: 31,
iconType: 'cloud',
iconColor: '#F0F8FF',
backgoundColor: dataColor.cloudBC,
temperature: '22℃',
weather: '多云'
},
{
lng: 120.6,
lat: 30.8,
iconType: 'cloud',
iconColor: '#F0F8FF',
backgoundColor: dataColor.cloudBC,
temperature: '22℃',
weather: '多云'
},
{
lng: 120.5,
lat: 31.3,
iconType: 'cloud',
iconColor: '#F0F8FF',
backgoundColor: dataColor.cloudBC,
temperature: '22℃',
weather: '多云'
},
{
lng: 121.3,
lat: 30.2,
iconType: 'smallRain',
iconColor: '#6EA0FF',
backgoundColor: dataColor.smallRainBC,
temperature: '22℃',
weather: '小雨'
},
{
lng: 121,
lat: 30.5,
iconType: 'smallRain',
iconColor: '#6EA0FF',
backgoundColor: dataColor.smallRainBC,
temperature: '22℃',
weather: '小雨'
},
{
lng: 120.6,
lat: 30,
iconType: 'middleRain',
iconColor: '#6495ED',
backgoundColor: dataColor.middleRainBC,
temperature: '24℃',
weather: '中雨'
},
{
lng: 120.2,
lat: 29.7,
iconType: 'smallRain',
iconColor: '#6EA0FF',
backgoundColor: dataColor.smallRainBC,
temperature: '22℃',
weather: '小雨'
},
{
lng: 121.7,
lat: 29.8,
iconType: 'middleRain',
iconColor: '#6495ED',
backgoundColor: dataColor.middleRainBC,
temperature: '24℃',
weather: '中雨'
},
{
lng: 121.5,
lat: 30,
iconType: 'hugeRain',
iconColor: '#4678D2',
backgoundColor: dataColor.bigRainBC,
temperature: '20℃',
weather: '大雨'
}
];
const fontFamily = 'iconfont';
const fontPath = '//at.alicdn.com/t/font_2534097_ao9soua2obv.woff2?t=1622021146076';
scene.addFontFace(fontFamily, fontPath);
scene.addIconFonts([
[ 'smallRain', '&#xe6f7;' ],
[ 'middleRain', '&#xe61c;' ],
[ 'hugeRain', '&#xe6a6;' ],
[ 'sun', '&#xe6da;' ],
[ 'cloud', '&#xe8da;' ]
]);
scene.on('loaded', () => {
const layer = new PointLayer()
.source(originData, {
parser: {
type: 'json',
x: 'lng',
y: 'lat'
}
})
.shape('circle')
.color('backgoundColor')
.size(42);
scene.addLayer(layer);
const pointIconFontLayer = new PointLayer({})
.source(originData, {
parser: {
type: 'json',
x: 'lng',
y: 'lat'
}
}
)
.shape('iconType', 'text')
.size(30)
.color('iconColor')
.style({
textAnchor: 'center', // 文本相对锚点的位置 center|left|right|top|bottom|top-left
textOffset: [ 38, 10 ], // 文本相对锚点的偏移量 [水平, 垂直]
fontFamily,
iconfont: true,
textAllowOverlap: true
});
scene.addLayer(pointIconFontLayer);
const textLayer = new PointLayer({})
.source(originData,
{
parser: {
type: 'json',
x: 'lng',
y: 'lat'
}
}
)
.shape('temperature', 'text')
.size(10)
.color('#ffffff')
.style({
textAnchor: 'center', // 文本相对锚点的位置 center|left|right|top|bottom|top-left
textOffset: [ 5, -55 ], // 文本相对锚点的偏移量 [水平, 垂直]
spacing: 2, // 字符间距
padding: [ 1, 1 ], // 文本包围盒 padding [水平,垂直],影响碰撞检测结果,避免相邻文本靠的太近
stroke: '#ffffff', // 描边颜色
strokeWidth: 0.3, // 描边宽度
strokeOpacity: 1.0,
fontFamily: 'Times New Roman',
textAllowOverlap: true
});
scene.addLayer(textLayer);
const textLayer2 = new PointLayer({})
.source(originData,
{
parser: {
type: 'json',
x: 'lng',
y: 'lat'
}
}
)
.shape('weather', 'text')
.size(14)
.color('#ffffff')
.style({
textAnchor: 'center', // 文本相对锚点的位置 center|left|right|top|bottom|top-left
textOffset: [ 5, -15 ], // 文本相对锚点的偏移量 [水平, 垂直]
spacing: 2, // 字符间距
padding: [ 1, 1 ], // 文本包围盒 padding [水平,垂直],影响碰撞检测结果,避免相邻文本靠的太近
stroke: '#ffffff', // 描边颜色
strokeWidth: 0.3, // 描边宽度
strokeOpacity: 1.0,
fontFamily: 'Times New Roman',
textAllowOverlap: true
});
scene.addLayer(textLayer2);
});

View File

@ -13,7 +13,26 @@
"filename": "polygon_text.js",
"title": "面数据标注",
"screenshot": "https://gw.alipayobjects.com/mdn/antv_site/afts/img/A*rCFqRp3iQosAAAAAAAAAAABkARQnAQ"
},
{
"filename": "iconfont.js",
"title": "图标标注",
"screenshot": "https://gw.alipayobjects.com/mdn/rms_23a451/afts/img/A*8PfqQ6-lQ0EAAAAAAAAAAAAAARQnAQ"
},
{
"filename": "iconfonts.js",
"title": "天气图标标注",
"screenshot": "https://gw.alipayobjects.com/mdn/rms_23a451/afts/img/A*JAhxTaabap4AAAAAAAAAAAAAARQnAQ"
},
{
"filename": "temperture.js",
"title": "气温图标标注",
"screenshot": "https://gw.alipayobjects.com/mdn/rms_23a451/afts/img/A*36umQaf_hVEAAAAAAAAAAAAAARQnAQ"
},
{
"filename": "updown.js",
"title": "走势图标标注",
"screenshot": "https://gw.alipayobjects.com/mdn/rms_23a451/afts/img/A*P5plS5ZsYZkAAAAAAAAAAAAAARQnAQ"
}
]
}

View File

@ -4,7 +4,7 @@ import { GaodeMap } from '@antv/l7-maps';
const scene = new Scene({
id: 'map',
map: new GaodeMap({
center: [ 120.19382669582967, 30.258134 ],
center: [ 110, 36 ],
pitch: 0,
style: 'light',
zoom: 3
@ -40,4 +40,5 @@ scene.on('loaded', () => {
scene.addLayer(pointLayer);
});
});

View File

@ -0,0 +1,103 @@
import { Scene, PointLayer, PolygonLayer, LineLayer } from '@antv/l7';
import { GaodeMap } from '@antv/l7-maps';
const scene = new Scene({
id: 'map',
map: new GaodeMap({
style: 'dark',
pitch: 40,
center: [ 118.8, 32.056 ],
zoom: 12.5
})
});
const fontFamily = 'iconfont';
const fontPath =
'//at.alicdn.com/t/font_2534097_x6rsov3i1g.woff2?t=1622107341225';
scene.addIconFont('icon', '&#xe69e;');
scene.addFontFace(fontFamily, fontPath);
const colors = [
'#87CEFA',
'#00BFFF',
'#7FFFAA',
'#00FF7F',
'#32CD32',
'#F0E68C',
'#FFD700',
'#FF7F50',
'#FF6347',
'#FF0000'
];
scene.on('loaded', () => {
fetch('https://gw.alipayobjects.com/os/bmw-prod/94763191-2816-4c1a-8d0d-8bcf4181056a.json')
.then(res => res.json())
.then(data => {
const filllayer = new PolygonLayer({
name: 'fill',
zIndex: 3
})
.source(data)
.shape('fill')
.color('count', [ '#f2f0f7', '#dadaeb', '#bcbddc', '#9e9ac8', '#756bb1', '#54278f' ])
.style({
opacity: 0.6
});
scene.addLayer(filllayer);
const linelayer = new LineLayer({
zIndex: 5,
name: 'line2'
})
.source(data)
.shape('line')
.size(1)
.color('#fff')
.style({
opacity: 0.3
});
scene.addLayer(linelayer);
const pointLayer = new PointLayer({
zIndex: 10
})
.source(data)
.shape('icon', 'text')
.size(30)
.color('count', t => {
const c = Number(t.replace('℃', ''));
return colors[Math.floor(((c - 18) / 16) * 10)];
})
.style({
textAnchor: 'center', // 文本相对锚点的位置 center|left|right|top|bottom|top-left
textOffset: [ 30, 5 ],
padding: [ 2, 2 ],
fontFamily,
iconfont: true
// textAllowOverlap: true
});
scene.addLayer(pointLayer);
const tempertureLayer = new PointLayer({
zIndex: 10
})
.source(data)
.shape('count', 'text')
.size(12)
.color('count', t => {
const c = Number(t.replace('℃', ''));
return colors[Math.floor(((c - 18) / 16) * 10)];
})
.style({
textAnchor: 'center', // 文本相对锚点的位置 center|left|right|top|bottom|top-left
textOffset: [ 35, 30 ],
padding: [ 1, 1 ]
});
scene.addLayer(tempertureLayer);
});
});

View File

@ -0,0 +1,83 @@
import { Scene, PointLayer, PolygonLayer, LineLayer } from '@antv/l7';
import { GaodeMap } from '@antv/l7-maps';
const scene = new Scene({
id: 'map',
map: new GaodeMap({
style: 'dark',
pitch: 40,
center: [ 118.8, 32.056 ],
zoom: 12.5
})
});
const fontFamily = 'iconfont';
const fontPath =
'//at.alicdn.com/t/font_2534097_bl34aphh10n.woff2?t=1622180820063';
scene.addIconFont('up', '&#xe61d;');
scene.addIconFont('down', '&#xe61e;');
scene.addFontFace(fontFamily, fontPath);
scene.on('loaded', () => {
fetch(
'https://gw.alipayobjects.com/os/bmw-prod/41802695-0f7e-4a81-ab16-539c4e39df0d.json'
)
.then(res => res.json())
.then(data => {
const filllayer = new PolygonLayer({
name: 'fill',
zIndex: 3
})
.source(data)
.shape('fill')
.color('count', [ '#f2f0f7', '#dadaeb', '#bcbddc', '#9e9ac8', '#756bb1', '#54278f' ])
.style({
opacity: 0.6
});
scene.addLayer(filllayer);
const linelayer = new LineLayer({
zIndex: 5,
name: 'line2'
})
.source(data)
.shape('line')
.size(1)
.color('#fff')
.style({
opacity: 0.3
});
scene.addLayer(linelayer);
const pointLayer = new PointLayer({
zIndex: 10
})
.source(data)
.shape('icon', 'text')
.size(15)
.color('count', n => (n > 0 ? '#0f0' : '#f00'))
.style({
textAnchor: 'center', // 文本相对锚点的位置 center|left|right|top|bottom|top-left
textOffset: [ 30, 5 ],
padding: [ 2, 2 ],
fontFamily,
iconfont: true
// textAllowOverlap: true
});
scene.addLayer(pointLayer);
const textLayer = new PointLayer({
zIndex: 10
})
.source(data)
.shape('count', 'text')
.size(12)
.color('count', n => (n > 0 ? '#0f0' : '#f00'))
.style({
textAnchor: 'center', // 文本相对锚点的位置 center|left|right|top|bottom|top-left
textOffset: [ 40, 10 ],
padding: [ 1, 1 ]
});
scene.addLayer(textLayer);
});
});

View File

@ -1,5 +1,5 @@
---
title: COVID-19 地图
title: COVID-19(新冠肺炎)地图
order: 0
---
[![github](https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*Nk9mQ48ZoZMAAAAAAAAAAABkARQnAQ)](https://github.com/antvis/L7)

View File

@ -42,13 +42,15 @@ const World = React.memo(function Map() {
options={{
autoFit: true,
}}
source={{
data,
// @ts-ignore
parser: {
type: 'json',
x: 'longitude',
y: 'latitude',
},
}
}}
shape={{
field: 'name',

View File

@ -6,8 +6,8 @@
"demos": [
{
"filename": "Point_image.tsx",
"title": "标注图",
"screenshot": "https://gw.alipayobjects.com/mdn/rms_855bab/afts/img/A*HQShTKuKq6wAAAAAAAAAAABkARQnAQ"
"title": "图片标注图",
"screenshot": "https://gw.alipayobjects.com/mdn/rms_23a451/afts/img/A*w-8iQpR1NEQAAAAAAAAAAAAAARQnAQ"
},
{
"filename": "marker.tsx",
@ -18,6 +18,6 @@
"filename": "popup.tsx",
"title": "信息窗",
"screenshot": "https://gw.alipayobjects.com/mdn/rms_855bab/afts/img/A*RWngRL7rZKQAAAAAAAAAAAAAARQnAQ"
},
}
]
}

View File

@ -96,6 +96,7 @@ const World = React.memo(function Map() {
{popupInfo && (
<Popup
key="popup"
// @ts-ignore
lnglat={popupInfo.lngLat}
option={{ closeButton: false, offsets: [0, 10] }}
>

View File

@ -1,17 +0,0 @@
import { Scale, Zoom, Scene } from '@antv/l7';
import { Mapbox } from '@antv/l7-maps';
const scene = new Scene({
id: 'map',
map: new Mapbox({
style: 'light',
pitch: 0,
center: [ 107.054293, 35.246265 ],
zoom: 4.056
})
});
scene.on('loaded', () => {
const zoomControl = new Zoom();
const scaleControl = new Scale();
scene.addControl(zoomControl);
scene.addControl(scaleControl);
});

View File

@ -5,14 +5,9 @@
},
"demos": [
{
"filename": "amap.js",
"title": "高德底图组件",
"screenshot": "https://gw.alipayobjects.com/mdn/antv_site/afts/img/A*yXzQRYcGTyoAAAAAAAAAAABkARQnAQ"
},
{
"filename": "mapbox.js",
"title": "MapBox底图组件",
"screenshot": "https://gw.alipayobjects.com/mdn/antv_site/afts/img/A*_SIYR50bbcoAAAAAAAAAAABkARQnAQ"
"filename": "layer_highlight.js",
"title": "交互高亮图层",
"screenshot": "https://gw.alipayobjects.com/mdn/rms_23a451/afts/img/A*jBzZRp_umUoAAAAAAAAAAAAAARQnAQ"
}
]
}

View File

@ -52,7 +52,8 @@ window.onLoad = function() {
});
};
const url = 'https://webapi.amap.com/maps?v=1.4.15&key=15cd8a57710d40c9b7c0e3cc120f1200&callback=onLoad';
// const url = 'https://webapi.amap.com/maps?v=1.4.15&key=15cd8a57710d40c9b7c0e3cc120f1200&callback=onLoad';
const url = 'https://webapi.amap.com/maps?v=2.0&key=ff533602d57df6f8ab3b0fea226ae52f&callback=onLoad';
const jsapi = document.createElement('script');
jsapi.charset = 'utf-8';
jsapi.src = url;

View File

@ -0,0 +1,58 @@
import { Scene, PointLayer } from '@antv/l7';
import { GaodeMap } from '@antv/l7-maps';
window.onLoad = function() {
const map = new AMap.Map('map', {
pitch: 0,
mapStyle: 'amap://styles/darkblue',
center: [ 121.435159, 31.256971 ],
zoom: 14.89,
minZoom: 10
});
const scene = new Scene({
id: 'map',
map: new GaodeMap({
mapInstance: map
})
});
scene.on('loaded', () => {
fetch(
'https://gw.alipayobjects.com/os/basement_prod/893d1d5f-11d9-45f3-8322-ee9140d288ae.json'
)
.then(res => res.json())
.then(data => {
const pointLayer = new PointLayer()
.source(data, {
parser: {
type: 'json',
x: 'longitude',
y: 'latitude'
}
})
.shape('name', [
'circle',
'triangle',
'square',
'pentagon',
'hexagon',
'octogon',
'hexagram',
'rhombus',
'vesica'
])
.size('unit_price', [ 10, 25 ])
.color('name', [ '#5B8FF9', '#5CCEA1', '#5D7092', '#F6BD16', '#E86452' ])
.style({
opacity: 0.3,
strokeWidth: 2
});
scene.addLayer(pointLayer);
});
});
};
const url = 'https://webapi.amap.com/maps?v=2.0&key=ff533602d57df6f8ab3b0fea226ae52f&callback=onLoad';
const jsapi = document.createElement('script');
jsapi.charset = 'utf-8';
jsapi.src = url;
document.head.appendChild(jsapi);

View File

@ -17,7 +17,12 @@
{
"filename": "amapInstance.js",
"title": "通过高德地图实例化",
"screenshot": "https://gw.alipayobjects.com/mdn/antv_site/afts/img/A*_SIYR50bbcoAAAAAAAAAAABkARQnAQ"
"screenshot": "https://gw.alipayobjects.com/mdn/rms_23a451/afts/img/A*C5d2RJ08hOkAAAAAAAAAAAAAARQnAQ"
},
{
"filename": "amapInstance2d.js",
"title": "通过高德地图实例化(2d)",
"screenshot": "https://gw.alipayobjects.com/mdn/rms_23a451/afts/img/A*C5d2RJ08hOkAAAAAAAAAAAAAARQnAQ"
}
]
}

View File

@ -44,7 +44,7 @@ export default class Control extends EventEmitter {
name: `${controlId++}`,
};
}
public setPosition(position: PositionName = "bottomright") {
public setPosition(position: PositionName = 'bottomright') {
// 考虑组件的自动布局,需要销毁重建
const controlService = this.controlService;
if (controlService) {

View File

@ -474,5 +474,3 @@
.amap-logo{
display: none !important;
}

View File

@ -56,7 +56,8 @@ export default class Marker extends EventEmitter {
// this.sceneSerive.getSceneContainer().appendChild(element as HTMLElement);
this.mapsService.getMarkerContainer().appendChild(element as HTMLElement);
this.registerMarkerEvent(element as HTMLElement);
this.mapsService.on('camerachange', this.update);
this.mapsService.on('camerachange', this.update); // 注册高德1.x 的地图事件监听
this.mapsService.on('viewchange', this.update); // 注册高德2.0 的地图事件监听
this.update();
this.added = true;
this.emit('added');

View File

@ -32,6 +32,7 @@ const VALID_PROPS = [
'cutoff',
'radius',
];
function getDefaultCharacterSet() {
const charSet = [];
for (let i = 32; i < 128; i++) {
@ -51,6 +52,7 @@ function setTextStyle(
ctx.textBaseline = 'middle';
// ctx.textAlign = 'left';
}
function populateAlphaChannel(alphaChannel: number[], imageData: ImageData) {
// populate distance value from tinySDF to image alpha channel
for (let i = 0; i < alphaChannel.length; i++) {
@ -60,7 +62,23 @@ function populateAlphaChannel(alphaChannel: number[], imageData: ImageData) {
@injectable()
export default class FontService implements IFontService {
public get scale() {
return HEIGHT_SCALE;
}
public get canvas(): HTMLCanvasElement {
const data = this.cache.get(this.key);
return data && data.data;
}
public get mapping(): IFontMapping {
const data = this.cache.get(this.key);
return data && data.mapping;
}
public fontAtlas: IFontAtlas;
// iconFontMap 记录用户设置的 iconfont unicode 和名称的键值关系
public iconFontMap: Map<string, string>;
private iconFontGlyphs: {
[key: string]: string;
} = {};
@ -79,46 +97,52 @@ export default class FontService implements IFontService {
sdf: true,
cutoff: DEFAULT_CUTOFF,
radius: DEFAULT_RADIUS,
iconfont: false,
};
this.key = '';
this.iconFontMap = new Map();
}
public addIconGlyphs(glyphs: IIconFontGlyph[]): void {
glyphs.forEach((glyph) => {
this.iconFontGlyphs[glyph.name] = glyph.unicode;
});
}
/**
* iconfont unicode
* @param fontUnicode
* @param name
*/
public addIconFont(name: string, fontUnicode: string): void {
this.iconFontMap.set(name, fontUnicode);
}
/**
* iconfont unicode map
* @param name
* @returns
*/
public getIconFontKey(name: string): string {
return this.iconFontMap.get(name) || name;
}
public getGlyph(name: string): string {
if (this.iconFontGlyphs[name]) {
return String.fromCharCode(parseInt(this.iconFontGlyphs[name], 16));
}
return '';
}
public get scale() {
return HEIGHT_SCALE;
}
public get canvas(): HTMLCanvasElement {
const data = this.cache.get(this.key);
return data && data.data;
}
public get mapping(): IFontMapping {
const data = this.cache.get(this.key);
return data && data.mapping;
}
public setFontOptions(option: Partial<IFontOptions>) {
this.fontOptions = {
...this.fontOptions,
...option,
};
// const oldKey = this.key;
this.key = this.getKey();
const charSet = this.getNewChars(this.key, this.fontOptions.characterSet);
const cachedFontAtlas = this.cache.get(this.key);
if (cachedFontAtlas && charSet.length === 0) {
// update texture with cached fontAtlas
return;
@ -137,6 +161,7 @@ export default class FontService implements IFontService {
public destroy(): void {
this.cache.clear();
this.iconFontMap.clear();
}
private generateFontAtlas(
@ -152,6 +177,7 @@ export default class FontService implements IFontService {
sdf,
radius,
cutoff,
iconfont,
} = this.fontOptions;
let canvas = cachedFontAtlas && cachedFontAtlas.data;
if (!canvas) {
@ -197,7 +223,22 @@ export default class FontService implements IFontService {
// tinySDF.size equals `fontSize + buffer * 2`
const imageData = ctx.getImageData(0, 0, tinySDF.size, tinySDF.size);
for (const char of characterSet) {
if (iconfont) {
// @ts-ignore
// const icon = eval(
// '("' + char.replace('&#x', '\\u').replace(';', '') + '")',
// );
const icon = String.fromCharCode(
parseInt(char.replace('&#x', '').replace(';', ''), 16),
);
const iconData = tinySDF.draw(icon);
populateAlphaChannel(iconData, imageData);
} else {
populateAlphaChannel(tinySDF.draw(char), imageData);
}
// populateAlphaChannel(tinySDF.draw(char), imageData);
// 考虑到描边,需要保留 sdf 的 buffer不能像 deck.gl 一样直接减去
ctx.putImageData(imageData, mapping[char].x, mapping[char].y);
}
@ -221,6 +262,7 @@ export default class FontService implements IFontService {
}
private getKey() {
return 'key';
const {
fontFamily,
fontWeight,
@ -236,6 +278,13 @@ export default class FontService implements IFontService {
return `${fontFamily} ${fontWeight} ${fontSize} ${buffer}`;
}
/**
*
* @param key
* @param characterSet
* @returns
* key
*/
private getNewChars(key: string, characterSet: string[]): string[] {
const cachedFontAtlas = this.cache.get(key);
if (!cachedFontAtlas) {

View File

@ -7,6 +7,7 @@ export interface IFontOptions {
sdf: boolean;
cutoff: number;
radius: number;
iconfont: boolean;
}
export interface IFontMappingOption {
characterSet: string[];
@ -29,6 +30,10 @@ export interface IFontMapping {
[key: string]: IFontMappingItem;
[key: number]: IFontMappingItem;
}
export interface IFontIconFontMapItem {
[key: string]: string;
}
export interface IFontAtlas {
xOffset: number;
yOffset: number;
@ -44,11 +49,14 @@ export interface IIconFontGlyph {
}
export interface IFontService {
mapping: IFontMapping;
iconFontMap: Map<string, string>;
fontAtlas: IFontAtlas;
canvas: HTMLCanvasElement;
scale: number;
init(): void;
addIconGlyphs(glyphs: IIconFontGlyph[]): void;
addIconFont(name: string, fontUnicode: string): void;
getIconFontKey(name: string): string;
getGlyph(name: string): string;
setFontOptions(option: Partial<IFontOptions>): void;
destroy(): void;

View File

@ -1,3 +1,5 @@
// @ts-ignore
import TinySDF from '@mapbox/tiny-sdf';
import { EventEmitter } from 'eventemitter3';
import { inject, injectable } from 'inversify';
import { TYPES } from '../../types';
@ -96,6 +98,9 @@ export default class IconService extends EventEmitter implements IIconService {
}
}
/**
* icon
*/
private updateIconAtlas() {
this.canvas.width = MAX_CANVAS_WIDTH;
this.canvas.height = this.canvasHeight;
@ -117,6 +122,9 @@ export default class IconService extends EventEmitter implements IIconService {
});
}
/**
* icon
*/
private updateIconMap() {
const { mapping, canvasHeight } = buildIconMaping(
this.iconData,

View File

@ -24,7 +24,8 @@ export default class CameraService implements ICameraService {
/**
* ViewMatrix
*/
private viewMatrixInverse: number[];
// private viewMatrixInverse: number[];
private viewMatrixInverse: mat4;
/**
*
@ -42,10 +43,12 @@ export default class CameraService implements ICameraService {
this.viewport = viewport;
// 计算逆矩阵
this.viewMatrixInverse = (mat4.invert(
mat4.create(),
(this.getViewMatrix() as unknown) as mat4,
) as unknown) as number[];
// this.viewMatrixInverse = (mat4.invert(
// mat4.create(), (this.getViewMatrix() as unknown) as mat4,
// ) as unknown) as number[];
this.viewMatrixInverse = mat4.create();
mat4.invert(this.viewMatrixInverse, viewport.getViewMatrix() as mat4);
this.cameraPosition = [
this.viewMatrixInverse[12],

View File

@ -79,6 +79,7 @@ const defaultLayerConfig: Partial<ILayerConfig> = {
duration: 4,
trailLength: 0.15,
},
forward: true, // 默认是正方向
};
// @see https://github.com/epoberezkin/ajv#options

View File

@ -3,6 +3,7 @@ import { inject, injectable } from 'inversify';
import { TYPES } from '../../types';
import { getDistanceScales } from '../../utils/project';
import { ICameraService } from '../camera/ICameraService';
// import { IMapService } from '../map/IMapService'
import {
CoordinateSystem,
ICoordinateSystemService,
@ -17,6 +18,10 @@ export default class CoordinateSystemService
@inject(TYPES.ICameraService)
private readonly cameraService: ICameraService;
// map.getCenter
// @inject(TYPES.IMapService)
// private readonly mapService: IMapService
/**
* 1. Web
* 2.

View File

@ -17,6 +17,7 @@ export enum CoordinateSystem {
P20 = 5.0,
P20_OFFSET = 6.0,
METER_OFFSET = 7.0,
P20_2 = 8.0,
}
// 后续传入 Shader 的变量
@ -27,6 +28,8 @@ export const CoordinateUniform = {
PixelsPerDegree: 'u_PixelsPerDegree',
PixelsPerDegree2: 'u_PixelsPerDegree2',
PixelsPerMeter: 'u_PixelsPerMeter',
Mvp: 'u_Mvp',
};
export interface ICoordinateSystemService {

View File

@ -130,6 +130,7 @@ export interface ILayer {
scale(field: string | number | IScaleOptions, cfg?: IScale): ILayer;
size(field: StyleAttrField, value?: StyleAttributeOption): ILayer;
color(field: StyleAttrField, value?: StyleAttributeOption): ILayer;
texture(field: StyleAttrField, value?: StyleAttributeOption): ILayer;
shape(field: StyleAttrField, value?: StyleAttributeOption): ILayer;
label(field: StyleAttrField, value?: StyleAttributeOption): ILayer;
animate(option: Partial<IAnimateOption> | boolean): ILayer;
@ -244,6 +245,8 @@ export interface ILayerConfig {
enableMultiPassRenderer: boolean;
passes: Array<string | [string, { [key: string]: unknown }]>;
forward: boolean; // 正方向
/**
*
*/
@ -274,6 +277,10 @@ export interface ILayerConfig {
*/
enableLighting: boolean;
animateOption: Partial<IAnimateOption>;
/**
* layer point text iconfont
*/
iconfont: boolean;
onHover(pickedFeature: IPickedFeature): void;
onClick(pickedFeature: IPickedFeature): void;
}

View File

@ -30,8 +30,10 @@ export interface IMapWrapper {
}
export interface IMapService<RawMap = {}> {
version: string;
map: RawMap;
init(): void;
initViewPort?(): void;
destroy(): void;
onCameraChanged(callback: (viewport: IViewport) => void): void;
// init map
@ -85,6 +87,12 @@ export interface IMapService<RawMap = {}> {
scale: [number, number, number],
origin: IMercator,
): number[];
lngLatToCoord?(lnglat: [number, number]): [number, number];
lngLatToCoords?(
lnglatArray: number[][][] | number[][],
): number[][][] | number[][] | number[][][] | number[][];
// lngLatToCoords?(lnglatArray: any): any;
getCustomCoordCenter?(): [number, number];
exportMap(type: 'jpg' | 'png'): string;
}
@ -178,6 +186,9 @@ export interface IMapCamera {
center: [number, number];
// 相机高度
cameraHeight: number;
cameraPosition?: [number, number, number];
up?: [number, number, number];
lookAt?: [number, number, number];
// 偏移原点,例如 P20 坐标系下
offsetOrigin: [number, number];
}

View File

@ -6,6 +6,8 @@ import { IRenderConfig } from '../renderer/IRendererService';
export interface ISceneService {
destroyed: boolean;
loaded: boolean;
fontFamily: string;
loadFont: boolean;
on(type: string, handle: (...args: any[]) => void): void;
off(type: string, handle: (...args: any[]) => void): void;
removeAllListeners(event?: string): this;
@ -16,6 +18,7 @@ export interface ISceneService {
getSceneContainer(): HTMLDivElement;
getMarkerContainer(): HTMLElement;
exportPng(type?: 'png' | 'jpg'): string;
addFontFace(fontname: string, fontpath: string): void;
destroy(): void;
}
// scene 事件

View File

@ -35,6 +35,10 @@ export default class Scene extends EventEmitter implements ISceneService {
public destroyed: boolean = false;
public loaded: boolean = false;
// loadFont 判断用户当前是否添加自定义字体
public loadFont: boolean = false;
// fontFamily 用户当前自己添加的字体的名称
public fontFamily: string = '';
@inject(TYPES.SceneID)
private readonly id: string;
@ -139,15 +143,32 @@ export default class Scene extends EventEmitter implements ISceneService {
*/
this.hooks.init.tapPromise('initMap', async () => {
// 等待首次相机同步
await new Promise((resolve) => {
await new Promise<void>((resolve) => {
this.map.onCameraChanged((viewport: IViewport) => {
this.cameraService.init();
this.cameraService.update(viewport);
if (this.map.version !== 'GAODE2.x') {
// not amap2
resolve();
}
});
if (this.map.version !== 'GAODE2.x') {
// not amap2
this.map.init();
} else {
// amap2
resolve();
}
});
if (this.map.version === 'GAODE2.x' && this.map.initViewPort) {
// amap2
await this.map.init();
this.map.initViewPort();
}
// this.controlService.addControls();
// 重新绑定非首次相机更新事件
this.map.onCameraChanged(this.handleMapCameraChanged);
this.map.addMarkerContainer();
@ -208,7 +229,6 @@ export default class Scene extends EventEmitter implements ISceneService {
// 执行异步并行初始化任务
// @ts-ignore
this.initPromise = this.hooks.init.promise();
this.render();
}
@ -222,15 +242,20 @@ export default class Scene extends EventEmitter implements ISceneService {
if (this.rendering || this.destroyed) {
return;
}
this.rendering = true;
// 首次初始化,或者地图的容器被强制销毁的需要重新初始化
if (!this.inited) {
// 还未初始化完成需要等待
await this.initPromise;
if (this.destroyed) {
this.destroy();
}
// @ts-ignore
if (this.loadFont && document.fonts) {
// @ts-ignore
await document.fonts.load(`24px ${this.fontFamily}`, 'L7text');
}
// FIXME: 初始化 marker 容器,可以放到 map 初始化方法中?
this.logger.info(' render inited');
this.layerService.initLayers();
@ -243,12 +268,30 @@ export default class Scene extends EventEmitter implements ISceneService {
// 尝试初始化未初始化的图层
this.layerService.renderLayers();
// 组件需要等待layer 初始化完成之后添加
this.logger.debug(`scene ${this.id} render`);
this.rendering = false;
}
/**
* 使 layer/point/text/iconfont
* @param fontFamily
* @param fontPath
*/
public addFontFace(fontFamily: string, fontPath: string): void {
this.fontFamily = fontFamily;
const style = document.createElement('style');
style.type = 'text/css';
style.innerText = `
@font-face{
font-family: '${fontFamily}';
src: url('${fontPath}') format('woff2'),
url('${fontPath}') format('woff'),
url('${fontPath}') format('truetype');
}`;
document.getElementsByTagName('head')[0].appendChild(style);
this.loadFont = true;
}
public getSceneContainer(): HTMLDivElement {
return this.$container as HTMLDivElement;
}

View File

@ -38,3 +38,33 @@ float pixelDistance(vec2 from, vec2 to) {
vec2 b1 = ProjectFlat(to);
return distance(a1, b1);
}
// gaode2.0
vec2 customProject(vec2 lnglat) { // 经纬度 => 平面坐标
float t = lnglat.x;
float e = lnglat.y;
float Sm = 180.0 / PI;
float Tm = 6378137.0;
float Rm = PI / 180.0;
float r = 85.0511287798;
e = max(min(r, e), -r);
t *= Rm;
e *= Rm;
e = log(tan(PI / 4.0 + e / 2.0));
return vec2(t * Tm, e * Tm);
}
vec2 unProjCustomCoord(vec2 point) { // 平面坐标 => 经纬度
float Sm = 57.29577951308232; //180 / Math.PI
float Tm = 6378137.0;
float t = point.x;
float e = point.y;
return vec2(t / Tm * Sm, (2.0 * atan(exp(e / Tm)) - PI / 2.0) * Sm);
}
float customPixelDistance(vec2 from, vec2 to) {
vec2 a1 = ProjectFlat(from);
vec2 b1 = ProjectFlat(to);
return distance(a1, b1);
}

View File

@ -10,6 +10,8 @@
#define COORDINATE_SYSTEM_P20_OFFSET 6.0
#define COORDINATE_SYSTEM_METER_OFFSET 7.0
#define COORDINATE_SYSTEM_P20_2 8.0
uniform mat4 u_ViewMatrix;
uniform mat4 u_ProjectionMatrix;
uniform mat4 u_ViewProjectionMatrix;
@ -28,6 +30,8 @@ uniform float u_DevicePixelRatio;
uniform float u_FocalDistance;
uniform vec3 u_CameraPosition;
// uniform mat4 u_Mvp;
// web mercator coords -> world coords
vec2 project_mercator(vec2 lnglat) {
float x = lnglat.x;
@ -69,6 +73,10 @@ vec3 reverse_offset_normal(vec3 vector) {
if (u_CoordinateSystem == COORDINATE_SYSTEM_P20 ||u_CoordinateSystem == COORDINATE_SYSTEM_P20_OFFSET ) {
return vector * vec3(1.0, -1.0, 1.0);
}
if (u_CoordinateSystem == COORDINATE_SYSTEM_P20_2) { // gaode2.0
return vector;
}
return vector;
}
@ -97,6 +105,18 @@ vec4 project_position(vec4 position) {
position.w
);
}
if(u_CoordinateSystem == COORDINATE_SYSTEM_P20_2) {
// return vec4(
// (position.xy * WORLD_SCALE * u_ZoomScale) * vec2(1., -1.),
// project_scale(position.z),
// position.w);
return vec4(
position.xy,
project_scale(position.z),
position.w);
}
return position;
// TODO: 瓦片坐标系 & 常规世界坐标系
@ -111,6 +131,10 @@ float project_pixel(float pixel) {
// P20 坐标系下,为了和 Web 墨卡托坐标系统一zoom 默认减1
return pixel * pow(2.0, (19.0 - u_Zoom));
}
if(u_CoordinateSystem == COORDINATE_SYSTEM_P20_2) {
// P20_2 坐标系下,为了和 Web 墨卡托坐标系统一zoom 默认减3
return pixel * pow(2.0, (19.0 - 3.0 - u_Zoom));
}
return pixel;
}
vec2 project_pixel(vec2 pixel) {
@ -118,6 +142,10 @@ vec2 project_pixel(vec2 pixel) {
// P20 坐标系下,为了和 Web 墨卡托坐标系统一zoom 默认减1
return pixel * pow(2.0, (19.0 - u_Zoom));
}
if(u_CoordinateSystem == COORDINATE_SYSTEM_P20_2) {
// P20_2 坐标系下,为了和 Web 墨卡托坐标系统一zoom 默认减3
return pixel * pow(2.0, (19.0 - 3.0 - u_Zoom));
}
return pixel * -1.;
}
@ -127,6 +155,7 @@ vec4 project_common_position_to_clipspace(vec4 position, mat4 viewProjectionMatr
// Needs to be divided with project_uCommonUnitsPerMeter
position.w *= u_PixelsPerMeter.z;
}
return viewProjectionMatrix * position + center;
}
@ -154,4 +183,3 @@ vec4 unproject_clipspace_to_position(vec4 clipspacePos, mat4 u_InverseViewProjec
bool isEqual( float a, float b) {
return a< b + 0.001 && a > b - 0.001;
}

View File

@ -35,7 +35,6 @@ export function buildMapping({
});
const rowHeight = fontHeight + buffer * 2;
return {
mapping,
xOffset: x,

View File

@ -14,6 +14,7 @@ attribute vec2 a_Uv;
varying vec2 v_texCoord;
varying vec4 v_Color;
uniform mat4 u_Mvp;
#pragma include "projection"
#pragma include "light"
@ -23,7 +24,12 @@ void main() {
vec4 pos = vec4(a_Position.xy, a_Position.z * a_Size, 1.0);
vec4 project_pos = project_position(pos);
v_texCoord = a_Uv;
// gl_Position = project_common_position_to_clipspace(vec4(project_pos.xyz, 1.0));
if(u_CoordinateSystem == COORDINATE_SYSTEM_P20_2) { // gaode2.x
gl_Position = u_Mvp * (vec4(project_pos.xyz, 1.0));
} else {
gl_Position = project_common_position_to_clipspace(vec4(project_pos.xyz, 1.0));
}
float lightWeight = calc_lighting(pos);
// v_Color = a_Color;

View File

@ -375,6 +375,16 @@ export default class BaseLayer<ChildLayerStyleOptions = {}> extends EventEmitter
return this;
}
// 为对应的图层传入纹理的编号名称point/image 在 shape 方法中传入纹理名称的方法并不通用)
public texture(
field: StyleAttributeField,
values?: StyleAttributeOption,
updateOptions?: Partial<IStyleAttributeUpdateOptions>,
) {
this.updateStyleAttribute('texture', field, values, updateOptions);
return this;
}
public rotate(
field: StyleAttributeField,
values?: StyleAttributeOption,
@ -482,7 +492,6 @@ export default class BaseLayer<ChildLayerStyleOptions = {}> extends EventEmitter
...this.rawConfig,
...rest,
};
if (this.container) {
this.updateLayerConfig(this.rawConfig);
this.styleNeedUpdate = true;
@ -500,6 +509,7 @@ export default class BaseLayer<ChildLayerStyleOptions = {}> extends EventEmitter
}
return this;
}
public render(): ILayer {
// if (
// this.needPick() &&

View File

@ -8,4 +8,7 @@ export interface ILineLayerStyleOptions {
lineType?: keyof typeof lineStyleType;
dashArray?: [number, number];
segmentNumber: number;
forward?: boolean;
lineTexture?: boolean;
iconStep?: number;
}

View File

@ -39,6 +39,7 @@ export function PointFillTriangulation(feature: IEncodeFeature) {
*/
export function PointExtrudeTriangulation(feature: IEncodeFeature) {
const { shape } = feature;
// console.log('PointExtrudeTriangulation', feature)
const { positions, index, normals } = getGeometry(
shape as ShapeType3D,
false,
@ -69,19 +70,45 @@ export function PointImageTriangulation(feature: IEncodeFeature) {
* @param feature feature
*/
export function LineTriangulation(feature: IEncodeFeature) {
const { coordinates } = feature;
const { coordinates, originCoordinates, version } = feature;
// let path = coordinates as number[][][] | number[][];
// if (!Array.isArray(path[0][0])) {
// path = [coordinates] as number[][][];
// }
const line = new ExtrudePolyline({
dash: true,
join: 'bevel',
});
if (version === 'GAODE2.x') {
// 处理高德2.0几何体构建
let path1 = coordinates as number[][][] | number[][]; // 计算位置
if (!Array.isArray(path1[0][0])) {
path1 = [coordinates] as number[][][];
}
let path2 = originCoordinates as number[][][] | number[][]; // 计算法线
if (!Array.isArray(path2[0][0])) {
path2 = [originCoordinates] as number[][][];
}
for (let i = 0; i < path1.length; i++) {
// 高德2.0在计算线时,需要使用经纬度计算发现,使用 customCoords.lnglatToCoords 计算的数据来计算顶点的位置
const item1 = path1[i];
const item2 = path2[i];
line.extrude_gaode2(item1 as number[][], item2 as number[][]);
}
} else {
// 处理非高德2.0的几何体构建
let path = coordinates as number[][][] | number[][];
if (!Array.isArray(path[0][0])) {
path = [coordinates] as number[][][];
}
const line = new ExtrudePolyline({
dash: true,
join: 'bevel', //
});
path.forEach((item: any) => {
// 处理带洞的多边形
line.extrude(item as number[][]);
});
}
const linebuffer = line.complex;
return {
vertices: linebuffer.positions, // [ x,y,z, distance, miter,total ]
@ -199,6 +226,7 @@ export function LineArcTriangulation(feature: IEncodeFeature) {
coordinates[1][0],
coordinates[1][1],
);
if (i !== segNum - 1) {
indexArray.push(
...[0, 1, 2, 1, 3, 2].map((v) => {
@ -214,6 +242,11 @@ export function LineArcTriangulation(feature: IEncodeFeature) {
};
}
/**
*
* @param feature
* @returns
*/
export function HeatmapTriangulation(feature: IEncodeFeature) {
const coordinates = feature.coordinates as number[];
if (coordinates.length === 2) {
@ -256,6 +289,7 @@ function getGeometry(shape: ShapeType3D, needFlat = false): IExtrudeGeomety {
: geometryShape.cylinder();
const geometry = extrude_PolygonNormal([path], needFlat);
GeometryCache[shape] = geometry;
// console.log('geometry', geometry)
return geometry;
}

View File

@ -63,7 +63,10 @@ export default class GridModel extends BaseModel {
},
size: 3,
update: (feature: IEncodeFeature, featureIdx: number) => {
const coordinates = feature.coordinates as number[];
// const coordinates = feature.coordinates as number[];
const coordinates = (feature.version === 'GAODE2.x'
? feature.originCoordinates
: feature.coordinates) as number[];
return [coordinates[0], coordinates[1], 0];
},
},

View File

@ -5,11 +5,11 @@ import {
IModel,
IModelUniform,
} from '@antv/l7-core';
import { aProjectFlat, Satistics, unProjectFlat } from '@antv/l7-utils';
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';
interface IHeatMapLayerStyleOptions {
opacity: number;
coverage: number;
@ -112,7 +112,11 @@ export default class Grid3DModel extends BaseModel {
},
size: 3,
update: (feature: IEncodeFeature, featureIdx: number) => {
const coordinates = feature.coordinates as number[];
const coordinates = (feature.version === 'GAODE2.x'
? feature.originCoordinates
: feature.coordinates) as number[];
// const coordinates = feature.coordinates as number[];
// const coordinates = feature.originCoordinates as number[];
return [coordinates[0], coordinates[1], 0];
},
},

View File

@ -9,6 +9,7 @@ import {
} from '@antv/l7-core';
import { generateColorRamp, IColorRamp } from '@antv/l7-utils';
import { mat4 } from 'gl-matrix';
import { inject, injectable } from 'inversify';
import BaseModel from '../../core/BaseModel';
import { HeatmapTriangulation } from '../../core/triangulation';
import heatmap3DFrag from '../shaders/heatmap_3d_frag.glsl';
@ -25,7 +26,7 @@ interface IHeatMapLayerStyleOptions {
angle: number;
rampColors: IColorRamp;
}
@injectable()
export default class HeatMapModel extends BaseModel {
protected texture: ITexture2D;
protected colorTexture: ITexture2D;
@ -256,13 +257,20 @@ export default class HeatMapModel extends BaseModel {
const {
opacity,
} = this.layer.getLayerConfig() as IHeatMapLayerStyleOptions;
const invert = mat4.invert(
mat4.create(),
mat4.fromValues(
// @ts-ignore
...this.cameraService.getViewProjectionMatrixUncentered(),
),
) as mat4;
// const invert = mat4.invert(
// mat4.create(),
// mat4.fromValues(
// // @ts-ignore
// ...this.cameraService.getViewProjectionMatrixUncentered(),
// ),
// ) as mat4;
const invert = mat4.create();
mat4.invert(
invert,
this.cameraService.getViewProjectionMatrixUncentered() as mat4,
);
this.colorModel.draw({
uniforms: {
u_opacity: opacity || 1.0,

View File

@ -65,7 +65,12 @@ export default class HexagonModel extends BaseModel {
},
size: 3,
update: (feature: IEncodeFeature, featureIdx: number) => {
const coordinates = feature.coordinates as number[];
// const coordinates = (feature.verison==='GAODE2.x'?feature.originoordinates:feature.coordinates) as number[];
const coordinates = (feature.version === 'GAODE2.x'
? feature.originCoordinates
: feature.coordinates) as number[];
// const coordinates = feature.coordinates as number[];
// const coordinates = feature.originCoordinates as number[];
return [coordinates[0], coordinates[1], 0];
},
},

View File

@ -9,8 +9,11 @@ uniform vec2 u_radius;
uniform float u_coverage: 0.9;
uniform float u_angle: 0;
uniform mat4 u_ModelMatrix;
uniform mat4 u_Mvp;
varying vec4 v_color;
uniform vec2 u_SceneCenterMKT;
#pragma include "projection"
#pragma include "project"
#pragma include "picking"
@ -20,9 +23,20 @@ void main() {
mat2 rotationMatrix = mat2(cos(u_angle), sin(u_angle), -sin(u_angle), cos(u_angle));
vec2 offset = a_Position.xy * u_radius * rotationMatrix * u_coverage ;
// vec2 lnglat = unProjectFlat(a_Pos.xy + offset);
// vec4 project_pos = project_position(vec4(lnglat, 0, 1.0));
// gl_Position = project_common_position_to_clipspace(project_pos);
if(u_CoordinateSystem == COORDINATE_SYSTEM_P20_2) { // gaode2.x
vec2 lnglat = unProjectFlat(a_Pos.xy + offset);
vec2 customLnglat = customProject(lnglat) - u_SceneCenterMKT; // 将经纬度转换为高德2.0需要的平面坐标
vec4 project_pos = project_position(vec4(customLnglat, 0, 1.0));
gl_Position = u_Mvp * (project_pos);
} else {
vec2 lnglat = unProjectFlat(a_Pos.xy + offset);
vec4 project_pos = project_position(vec4(lnglat, 0, 1.0));
gl_Position = project_common_position_to_clipspace(project_pos);
}
setPickingColor(a_PickingColor);
}

View File

@ -11,5 +11,4 @@ void main(){
gl_FragColor = color;
// gl_FragColor.a = color.a * smoothstep(0.1,0.2,intensity)* u_opacity;
gl_FragColor.a = color.a * smoothstep(0.,0.1,intensity) * u_opacity;
}

View File

@ -8,6 +8,7 @@ uniform mat4 u_InverseViewProjectionMatrix;
uniform mat4 u_ViewProjectionMatrixUncentered;
varying float v_intensity;
vec2 toBezier(float t, vec2 P0, vec2 P1, vec2 P2, vec2 P3) {
float t2 = t * t;
float one_minus_t = 1.0 - t;
@ -21,22 +22,21 @@ vec2 toBezier(float t, vec4 p){
void main() {
v_texCoord = a_Uv;
vec2 pos =(a_Uv * vec2(2.0) - vec2(1.0));
vec2 pos = a_Uv * vec2(2.0) - vec2(1.0); // 将原本 0 -> 1 的 uv 转换为 -1 -> 1 的标准坐标空间NDC
vec4 p1 = vec4(pos, 0.0, 1.0); // x/y 平面上的点z == 0可以认为是三维上的点被投影到平面后的点
vec4 p2 = vec4(pos, 1.0, 1.0); // 平行于x/y平面、z==1 的平面上的点
vec4 p1 = vec4(pos, 0.0, 1.0);
vec4 p2 = vec4(pos, 1.0, 1.0);
vec4 inverseP1 = u_InverseViewProjectionMatrix * p1;
vec4 inverseP1 = u_InverseViewProjectionMatrix * p1; // 根据视图投影矩阵的逆矩阵平面上的反算出三维空间中的点p1平面上的点
vec4 inverseP2 = u_InverseViewProjectionMatrix * p2;
inverseP1 = inverseP1 / inverseP1.w;
inverseP1 = inverseP1 / inverseP1.w; // 归一化操作(归一化后为世界坐标)
inverseP2 = inverseP2 / inverseP2.w;
float zPos = (0.0 - inverseP1.z) / (inverseP2.z - inverseP1.z);
float zPos = (0.0 - inverseP1.z) / (inverseP2.z - inverseP1.z); // ??
vec4 position = inverseP1 + zPos * (inverseP2 - inverseP1);
vec4 b= vec4(0.5000, 0, 1, 0.5000);
vec4 b= vec4(0.5000, 0.0, 1.0, 0.5000);
float fh;
v_intensity = texture2D(u_texture, v_texCoord).r;

View File

@ -7,6 +7,7 @@ uniform float u_radius;
varying vec2 v_extrude;
varying float v_weight;
uniform mat4 u_ModelMatrix;
uniform mat4 u_Mvp;
#define GAUSS_COEF 0.3989422804014327
@ -24,5 +25,11 @@ void main(){
vec2 offset = project_pixel(v_extrude * u_radius);
vec4 project_pos = project_position(vec4(a_Position.xy, 0.0, 1.0));
// gl_Position = project_common_position_to_clipspace(vec4(project_pos.xy + offset, 0.0, 1.0));
if(u_CoordinateSystem == COORDINATE_SYSTEM_P20_2) { // gaode2.x
gl_Position = u_Mvp * (vec4(project_pos.xy + offset, 0.0, 1.0));
} else {
gl_Position = project_common_position_to_clipspace(vec4(project_pos.xy + offset, 0.0, 1.0));
}
}

View File

@ -11,21 +11,54 @@ uniform vec2 u_radius;
uniform float u_coverage: 0.9;
uniform float u_angle: 0;
uniform mat4 u_ModelMatrix;
uniform mat4 u_Mvp;
varying vec4 v_color;
uniform vec2 u_SceneCenterMKT;
#pragma include "projection"
#pragma include "project"
#pragma include "light"
#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);
// vec2 lnglat = unProjectFlat(a_Pos.xy + offset); // 实际的经纬度
// vec2 lnglat = (a_Pos.xy + offset);
// vec4 project_pos = project_position(vec4(lnglat, a_Position.z * a_Size, 1.0));
// gl_Position = project_common_position_to_clipspace(project_pos);
// float lightWeight = calc_lighting(project_pos);
// v_color =vec4(a_Color.rgb*lightWeight, a_Color.w);
if(u_CoordinateSystem == COORDINATE_SYSTEM_P20_2) { // gaode2.x
// vec2 lnglat = (a_Pos.xy + offset);
// 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 = u_Mvp * vec4(lnglat , a_Position.z * a_Size, 1.0);
vec2 lnglat = unProjectFlat(a_Pos.xy + offset); // 经纬度
vec2 customLnglat = customProject(lnglat) - u_SceneCenterMKT; // 将经纬度转换为高德2.0需要的平面坐标
vec4 project_pos = project_position(vec4(customLnglat, 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 = u_Mvp * vec4(customLnglat , a_Position.z * a_Size, 1.0);
} else {
vec2 lnglat = unProjectFlat(a_Pos.xy + offset); // 实际的经纬度
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);
}

View File

@ -9,8 +9,11 @@ uniform vec2 u_radius;
uniform float u_coverage: 0.9;
uniform float u_angle: 0;
uniform mat4 u_ModelMatrix;
uniform mat4 u_Mvp;
varying vec4 v_color;
uniform vec2 u_SceneCenterMKT;
#pragma include "projection"
#pragma include "project"
#pragma include "picking"
@ -21,7 +24,18 @@ 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, 0, 1.0));
// gl_Position = project_common_position_to_clipspace(vec4(project_pos.xy, 0., 1.0));
if(u_CoordinateSystem == COORDINATE_SYSTEM_P20_2) { // gaode2.x
// gl_Position = u_Mvp * (vec4(project_pos.xy, 0., 1.0));
// gl_Position = u_Mvp * (vec4(a_Pos.xy + offset, 0., 1.0));
vec2 customLnglat = customProject(lnglat) - u_SceneCenterMKT;
vec4 project_pos = project_position(vec4(customLnglat, 0, 1.0));
gl_Position = u_Mvp * vec4(project_pos.xy, 0.0, 1.0);
} else {
vec4 project_pos = project_position(vec4(lnglat, 0, 1.0));
gl_Position = project_common_position_to_clipspace(vec4(project_pos.xy, 0., 1.0));
}
setPickingColor(a_PickingColor);
}

View File

@ -1,5 +1,6 @@
precision highp float;
uniform mat4 u_ModelMatrix;
uniform mat4 u_Mvp;
attribute vec3 a_Position;
attribute vec2 a_Uv;
varying vec2 v_texCoord;
@ -7,5 +8,10 @@ varying vec2 v_texCoord;
void main() {
v_texCoord = a_Uv;
vec4 project_pos = project_position(vec4(a_Position, 1.0));
// gl_Position = project_common_position_to_clipspace(vec4(project_pos.xy,0., 1.0));
if(u_CoordinateSystem == COORDINATE_SYSTEM_P20_2) { // gaode2.x
gl_Position = u_Mvp * (vec4(project_pos.xy,0., 1.0));
} else {
gl_Position = project_common_position_to_clipspace(vec4(project_pos.xy,0., 1.0));
}
}

View File

@ -23,6 +23,7 @@ export default class ArcModel extends BaseModel {
opacity,
lineType = 'solid',
dashArray = [10, 5],
forward = true,
} = this.layer.getLayerConfig() as ILineLayerStyleOptions;
if (dashArray.length === 2) {
dashArray.push(0, 0);
@ -33,6 +34,7 @@ export default class ArcModel extends BaseModel {
u_line_type: lineStyleObj[lineType || 'solid'],
u_dash_array: dashArray,
u_blur: 0.9,
u_lineDir: forward ? 1 : -1,
};
}

View File

@ -3,9 +3,11 @@ import {
gl,
IAnimateOption,
IEncodeFeature,
IImage,
ILayerConfig,
IModel,
IModelUniform,
ITexture2D,
} from '@antv/l7-core';
import BaseModel from '../../core/BaseModel';
@ -18,19 +20,32 @@ const lineStyleObj: { [key: string]: number } = {
dash: 1.0,
};
export default class LineModel extends BaseModel {
protected texture: ITexture2D;
public getUninforms(): IModelUniform {
const {
opacity,
lineType = 'solid',
dashArray = [10, 5, 0, 0],
lineTexture = false,
iconStep = 100,
} = this.layer.getLayerConfig() as ILineLayerStyleOptions;
if (dashArray.length === 2) {
dashArray.push(0, 0);
}
if (this.rendererService.getDirty()) {
this.texture.bind();
}
return {
u_opacity: opacity || 1.0,
u_line_type: lineStyleObj[lineType],
u_dash_array: dashArray,
u_texture: this.texture, // 贴图
u_line_texture: lineTexture ? 1.0 : 0.0, // 传入线的标识
u_icon_step: iconStep,
u_textSize: [1024, this.iconService.canvasHeight || 128],
};
}
public getAnimateUniforms(): IModelUniform {
@ -42,9 +57,33 @@ export default class LineModel extends BaseModel {
}
public initModels(): IModel[] {
// const { createTexture2D } = this.rendererService;
// this.texture = createTexture2D({
// height: 0,
// width: 0,
// });
// let url = 'https://gw-office.alipayobjects.com/bmw-prod/e91c3630-b79e-45a3-a2b9-feee4b4ccd41.svg'
// this.loadImage(url).then((img) => {
// this.texture = createTexture2D({
// data: img as HTMLImageElement,
// width: (img as HTMLImageElement).width,
// height: (img as HTMLImageElement).height,
// });
// this.layerService.renderLayers();
// })
this.updateTexture();
this.iconService.on('imageUpdate', this.updateTexture);
return this.buildModels();
}
public clearModels() {
if (this.texture) {
this.texture.destroy();
}
this.iconService.off('imageUpdate', this.updateTexture);
}
public buildModels(): IModel[] {
return [
this.layer.buildLayerModel({
@ -182,5 +221,68 @@ export default class LineModel extends BaseModel {
},
},
});
this.styleAttributeService.registerStyleAttribute({
name: 'uv',
type: AttributeType.Attribute,
descriptor: {
name: 'a_iconMapUV',
buffer: {
// give the WebGL driver a hint that this buffer may change
usage: gl.DYNAMIC_DRAW,
data: [],
type: gl.FLOAT,
},
size: 2,
update: (
feature: IEncodeFeature,
featureIdx: number,
vertex: number[],
attributeIdx: number,
) => {
const iconMap = this.iconService.getIconMap();
const { texture } = feature;
const { x, y } = iconMap[texture as string] || { x: 0, y: 0 };
return [x, y];
},
},
});
}
private loadImage(url: IImage) {
return new Promise((resolve, reject) => {
if (url instanceof HTMLImageElement) {
resolve(url);
return;
}
const image = new Image();
image.crossOrigin = 'anonymous';
image.onload = () => {
resolve(image);
};
image.onerror = () => {
reject(new Error('Could not load image at ' + url));
};
image.src = url instanceof File ? URL.createObjectURL(url) : url;
});
}
private updateTexture = () => {
const { createTexture2D } = this.rendererService;
if (this.texture) {
this.texture.update({
data: this.iconService.getCanvas(),
});
this.layer.render();
return;
}
this.texture = createTexture2D({
data: this.iconService.getCanvas(),
mag: gl.NEAREST,
min: gl.NEAREST,
premultiplyAlpha: false,
width: 1024,
height: this.iconService.canvasHeight || 128,
});
};
}

View File

@ -7,6 +7,7 @@ attribute vec4 a_Color;
attribute float a_Size;
uniform mat4 u_ModelMatrix;
uniform mat4 u_Mvp;
uniform float segmentNumber;
uniform vec4 u_aimate: [ 0, 2., 1.0, 0.2 ];
varying vec4 v_color;
@ -85,6 +86,11 @@ void main() {
vec2 offset = getExtrusionOffset((next.xy - curr.xy) * indexDir, a_Position.y);
v_normal = getNormal((next.xy - curr.xy) * indexDir, a_Position.y);
// gl_Position = project_common_position_to_clipspace(vec4(curr.xy + project_pixel(offset), curr.z, 1.0));
if(u_CoordinateSystem == COORDINATE_SYSTEM_P20_2) { // gaode2.x
gl_Position = u_Mvp * (vec4(curr.xy + project_pixel(offset), curr.z, 1.0));
} else {
gl_Position = project_common_position_to_clipspace(vec4(curr.xy + project_pixel(offset), curr.z, 1.0));
}
setPickingColor(a_PickingColor);
}

View File

@ -35,5 +35,16 @@ void main() {
alpha = smoothstep(0., 1., alpha);
gl_FragColor.a *= alpha;
}
// if(u_line_texture == LineTexture) { // while load texture
// //v_u; // 水平
// float v = length(v_offset)/(v_a); // 横向
// vec2 uv= v_iconMapUV / u_textSize + vec2(v_u, v) / u_textSize * 64.;
// // gl_FragColor = vec4(v_u, v, 0.0, 1.0);
// // gl_FragColor = vec4(1.0, 0.0, 0.0, v_u);
// gl_FragColor = filterColor(gl_FragColor + texture2D(u_texture, uv));
// } else {
// gl_FragColor = filterColor(gl_FragColor);
// }
gl_FragColor = filterColor(gl_FragColor);
}

View File

@ -6,6 +6,7 @@ attribute vec3 a_Position;
attribute vec4 a_Instance;
attribute float a_Size;
uniform mat4 u_ModelMatrix;
uniform mat4 u_Mvp;
uniform float segmentNumber;
uniform vec4 u_aimate: [ 0, 2., 1.0, 0.2 ];
varying vec4 v_color;
@ -68,8 +69,30 @@ float getAngularDist (vec2 source, vec2 target) {
sin_half_delta.x * sin_half_delta.x;
return 2.0 * atan(sqrt(a), sqrt(1.0 - a));
}
vec2 midPoint(vec2 source, vec2 target) {
vec2 center = target - source;
float r = length(center);
float theta = atan(center.y, center.x);
float thetaOffset = 0.314;
float r2 = r / 2.0 / cos(thetaOffset);
float theta2 = theta + thetaOffset;
vec2 mid = vec2(r2*cos(theta2) + source.x, r2*sin(theta2) + source.y);
return mid;
}
float bezier3(vec3 arr, float t) {
float ut = 1. - t;
return (arr.x * ut + arr.y * t) * ut + (arr.y * ut + arr.z * t) * t;
}
vec2 interpolate (vec2 source, vec2 target, float angularDist, float t) {
// if the angularDist is PI, linear interpolation is applied. otherwise, use spherical interpolation
if(u_CoordinateSystem == COORDINATE_SYSTEM_P20_2) { // gaode2.x
vec2 mid = midPoint(source, target);
vec3 x = vec3(source.x, mid.x, target.x);
vec3 y = vec3(source.y, mid.y, target.y);
return vec2(bezier3(x ,t), bezier3(y,t));
}else {
if(abs(angularDist - PI) < 0.001) {
return (1.0 - t) * source + t * target;
}
@ -84,6 +107,7 @@ vec2 interpolate (vec2 source, vec2 target, float angularDist, float t) {
float z = a * sin_source.y + b * sin_target.y;
return vec2(atan(y, x), atan(z, sqrt(x * x + y * y)));
}
}
void main() {
v_color = a_Color;
@ -108,7 +132,13 @@ void main() {
v_normal = getNormal((next.xy - curr.xy) * indexDir, a_Position.y);
vec2 offset = project_pixel(getExtrusionOffset((next.xy - curr.xy) * indexDir, a_Position.y));
// vec4 project_pos = project_position(vec4(curr.xy, 0, 1.0));
// gl_Position = project_common_position_to_clipspace(vec4(curr.xy + offset, curr.z, 1.0));
if(u_CoordinateSystem == COORDINATE_SYSTEM_P20_2) { // gaode2.x
gl_Position = u_Mvp * (vec4(curr.xy + offset, curr.z, 1.0));
} else {
gl_Position = project_common_position_to_clipspace(vec4(curr.xy + offset, curr.z, 1.0));
}
setPickingColor(a_PickingColor);
}

View File

@ -1,11 +1,13 @@
#define LineTypeSolid 0.0
#define LineTypeDash 1.0
#define Animate 0.0
// #define LineTexture 1.0
attribute vec4 a_Color;
attribute vec3 a_Position;
attribute vec4 a_Instance;
attribute float a_Size;
uniform mat4 u_ModelMatrix;
uniform mat4 u_Mvp;
uniform float segmentNumber;
uniform vec4 u_aimate: [ 0, 2., 1.0, 0.2 ];
varying vec4 v_color;
@ -14,7 +16,17 @@ varying vec2 v_normal;
varying float v_distance_ratio;
uniform float u_line_type: 0.0;
uniform vec4 u_dash_array: [10.0, 5., 0, 0];
uniform float u_lineDir: 1.0;
varying vec4 v_dash_array;
// uniform float u_icon_step: 100;
// uniform float u_line_texture;
// varying float v_u;
// varying vec2 v_offset;
// varying float v_a;
// attribute vec2 a_iconMapUV;
// varying vec2 v_iconMapUV;
#pragma include "projection"
#pragma include "project"
#pragma include "picking"
@ -31,7 +43,14 @@ vec2 midPoint(vec2 source, vec2 target) {
float r2 = r / 2.0 / cos(thetaOffset);
float theta2 = theta + thetaOffset;
vec2 mid = vec2(r2*cos(theta2) + source.x, r2*sin(theta2) + source.y);
if(u_lineDir == 1.0) { // 正向
return mid;
} else { // 逆向
// (mid + vmin)/2 = (s + t)/2
vec2 vmid = source + target - mid;
return vmid;
}
// return mid;
}
float getSegmentRatio(float index) {
return smoothstep(0.0, 1.0, index / (segmentNumber - 1.));
@ -61,10 +80,12 @@ vec2 getNormal(vec2 line_clipspace, float offset_direction) {
void main() {
v_color = a_Color;
vec2 source = a_Instance.rg;
vec2 target = a_Instance.ba;
float segmentIndex = a_Position.x;
float segmentRatio = getSegmentRatio(segmentIndex);
float indexDir = mix(-1.0, 1.0, step(segmentIndex, 0.0));
float nextSegmentRatio = getSegmentRatio(segmentIndex + indexDir);
if(u_line_type == LineTypeDash) {
@ -74,12 +95,40 @@ void main() {
}
if(u_aimate.x == Animate) {
v_distance_ratio = segmentIndex / segmentNumber;
if(u_lineDir != 1.0) {
v_distance_ratio = 1.0 - v_distance_ratio;
}
}
vec4 curr = project_position(vec4(interpolate(source, target, segmentRatio), 0.0, 1.0));
vec4 next = project_position(vec4(interpolate(source, target, nextSegmentRatio), 0.0, 1.0));
v_normal = getNormal((next.xy - curr.xy) * indexDir, a_Position.y);
vec2 offset = project_pixel(getExtrusionOffset((next.xy - curr.xy) * indexDir, a_Position.y));
// if(LineTexture == u_line_texture) { // 开启贴图模式
// v_iconMapUV = a_iconMapUV;
// float arctotal_Distance = length(source - target);
// float pixelLen = project_pixel(u_icon_step);
// v_u = fract(segmentRatio * (floor(arctotal_Distance/pixelLen)));
// // v_u = fract(segmentIndex/(segmentNumber) * (2.0));
// // v_u = fract(segmentIndex/(segmentNumber - 1.0) * 1.0 + 0.3);
// // v_u = fract(mod(1.0- v_distance_ratio, 0.2)* (1.0/ 0.5));
// // v_u = fract(clamp(v_u, 0.0, 1.0)*2.0);
// // v_u = fract(((segmentIndex * indexDir) / (segmentNumber - 1.)) * (floor(arctotal_Distance/pixelLen)));
// // float s = 6.0;
// // float l = segmentNumber/s;
// // v_u = mod(segmentIndex, l) / (segmentNumber/s);
// v_a = project_pixel(a_Size);
// v_offset = offset + offset * sign(a_Position.y);
// }
// gl_Position = project_common_position_to_clipspace(vec4(curr.xy + offset, 0, 1.0));
if(u_CoordinateSystem == COORDINATE_SYSTEM_P20_2) { // gaode2.x
gl_Position = u_Mvp * (vec4(curr.xy + offset, 0, 1.0));
} else {
gl_Position = project_common_position_to_clipspace(vec4(curr.xy + offset, 0, 1.0));
}
setPickingColor(a_PickingColor);
}

View File

@ -1,12 +1,17 @@
#define LineTypeSolid 0.0
#define LineTypeDash 1.0
#define Animate 0.0
#define LineTexture 1.0
uniform float u_blur : 0.99;
uniform float u_line_type: 0.0;
uniform float u_opacity : 1.0;
varying vec4 v_color;
varying vec2 v_normal;
// line texture
uniform float u_line_texture;
uniform sampler2D u_texture;
uniform vec2 u_textSize;
// dash
uniform float u_dash_offset : 0.0;
@ -15,19 +20,27 @@ varying float v_distance_ratio;
varying vec4 v_dash_array;
varying float v_side;
varying float v_distance;
varying vec2 v_offset;
varying float v_a;
varying float v_pixelLen;
varying vec2 v_iconMapUV;
varying float v_strokeWidth;
#pragma include "picking"
uniform float u_time;
uniform vec4 u_aimate: [ 0, 2., 1.0, 0.2 ];
uniform vec4 u_aimate: [ 0, 2., 1.0, 0.2 ]; // 控制运动
// [animate, duration, interval, trailLength],
void main() {
float animateSpeed = 0.0; // 运动速度
gl_FragColor = v_color;
// anti-alias
// float blur = 1.0 - smoothstep(u_blur, 1., length(v_normal.xy));
gl_FragColor.a *= u_opacity;
gl_FragColor.a *= u_opacity; // 全局透明度
if(u_aimate.x == Animate) {
float alpha =1.0 - fract( mod(1.0- v_distance_ratio, u_aimate.z)* (1.0/ u_aimate.z) + u_time / u_aimate.y);
animateSpeed = u_time / u_aimate.y;
float alpha =1.0 - fract( mod(1.0- v_distance_ratio, u_aimate.z)* (1.0/ u_aimate.z) + animateSpeed);
alpha = (alpha + u_aimate.w -1.0) / u_aimate.w;
alpha = smoothstep(0., 1., alpha);
gl_FragColor.a *= alpha;
@ -43,5 +56,28 @@ void main() {
// gl_FragColor.a *=(1.0- step(v_dash_array.x, mod(v_distance_ratio, dashLength)));
}
if(u_line_texture == LineTexture) { // while load texture
float u = fract(mod(v_distance, v_pixelLen)/v_pixelLen - animateSpeed);
float v = length(v_offset)/(v_a*2.0);
v = max(smoothstep(0.95, 1.0, v), v);
vec2 uv= v_iconMapUV / u_textSize + vec2(u, v) / u_textSize * 64.;
// gl_FragColor = filterColor(gl_FragColor + texture2D(u_texture, vec2(u, v)));
gl_FragColor = filterColor(gl_FragColor + texture2D(u_texture, uv));
} else {
gl_FragColor = filterColor(gl_FragColor);
}
// gl_FragColor = filterColor(vec4(1.0, 0.0, 0.0, 1.0));
// float r = max(smoothstep( 0.95, 1.0, v_strokeWidth/(v_a*2.0)), v_strokeWidth/(v_a*2.0));
// if(rV < r || rV > 1.0 - r) {
// gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
// }
// float v = length(v_offset)/(v_a*2.0);
// if(v > 0.9) {
// gl_FragColor = vec4(0.17647, 0.43921568, 0.2, 1.0);
// } else if(v < 0.1) {
// gl_FragColor = vec4(0.17647, 0.43921568, 0.2, 1.0);
// }
// gl_FragColor = filterColor(gl_FragColor);
}

View File

@ -8,14 +8,18 @@ attribute vec2 a_Size;
attribute vec3 a_Normal;
attribute vec3 a_Position;
attribute vec2 a_iconMapUV;
// dash line
attribute float a_Total_Distance;
attribute float a_Distance;
uniform mat4 u_ModelMatrix;
uniform mat4 u_Mvp;
uniform float u_line_type: 0.0;
uniform vec4 u_dash_array: [10.0, 5.0, 0];
uniform vec4 u_dash_array: [10.0, 5., 0, 0];
uniform vec4 u_aimate: [ 0, 2., 1.0, 0.2 ];
uniform float u_icon_step: 100;
#pragma include "projection"
#pragma include "picking"
@ -26,23 +30,52 @@ varying vec2 v_normal;
varying float v_distance_ratio;
varying float v_side;
varying float v_distance;
varying vec2 v_offset;
varying float v_size;
varying float v_a;
varying float v_pixelLen;
varying vec2 v_iconMapUV;
// varying float v_strokeWidth;
void main() {
v_iconMapUV = a_iconMapUV;
v_distance = a_Distance;
v_pixelLen = project_pixel(u_icon_step);
if(u_line_type == LineTypeDash) {
v_distance_ratio = a_Distance / a_Total_Distance;
// v_distance_ratio = 0.01;
v_dash_array = pow(2.0, 20.0 - u_Zoom) * u_dash_array / a_Total_Distance;
}
if(u_aimate.x == Animate) {
v_distance_ratio = a_Distance / a_Total_Distance;
}
v_normal = vec2(reverse_offset_normal(a_Normal) * sign(a_Miter));
v_color = a_Color;
v_a = project_pixel(a_Size.x);
vec3 size = a_Miter * setPickingSize(a_Size.x) * reverse_offset_normal(a_Normal);
vec2 offset = project_pixel(size.xy);
// v_strokeWidth = project_pixel(2.0);
v_offset = offset + offset * sign(a_Miter);
v_side = a_Miter * a_Size.x;
vec4 project_pos = project_position(vec4(a_Position.xy, 0, 1.0));
// gl_Position = project_common_position_to_clipspace(vec4(project_pos.xy + offset, a_Size.y, 1.0));
if(u_CoordinateSystem == COORDINATE_SYSTEM_P20_2) { // gaode2.x
// gl_Position = u_Mvp * (vec4(project_pos.xy + offset, a_Size.y, 1.0));
gl_Position = u_Mvp * (vec4(project_pos.xy + offset, a_Size.y / 10.0, 1.0)); // 额外除 10.0 是为了和gaode1.x的高度兼容
} else {
gl_Position = project_common_position_to_clipspace(vec4(project_pos.xy + offset, a_Size.y, 1.0));
}
setPickingColor(a_PickingColor);
}

View File

@ -1,16 +1,20 @@
import {
IEncodeFeature,
IFontService,
IGlobalConfigService,
ILayer,
ILayerPlugin,
ILngLat,
ILogService,
IMapService,
IParseDataItem,
IStyleAttribute,
IStyleAttributeService,
TYPES,
} from '@antv/l7-core';
import { rgb2arr } from '@antv/l7-utils';
import { rgb2arr, unProjectFlat } from '@antv/l7-utils';
import { inject, injectable } from 'inversify';
import { cloneDeep } from 'lodash';
@injectable()
export default class DataMappingPlugin implements ILayerPlugin {
@ -20,6 +24,12 @@ export default class DataMappingPlugin implements ILayerPlugin {
@inject(TYPES.ILogService)
private readonly logger: ILogService;
@inject(TYPES.IMapService)
private readonly mapService: IMapService;
@inject(TYPES.IFontService)
private readonly fontService: IFontService;
public apply(
layer: ILayer,
{
@ -99,13 +109,15 @@ export default class DataMappingPlugin implements ILayerPlugin {
data: IParseDataItem[],
predata?: IEncodeFeature[],
): IEncodeFeature[] {
return data.map((record: IParseDataItem, i) => {
// console.log('data', data[0])
const mappedData = data.map((record: IParseDataItem, i) => {
const preRecord = predata ? predata[i] : {};
const encodeRecord: IEncodeFeature = {
id: record._id,
coordinates: record.coordinates,
...preRecord,
};
attributes
.filter((attribute) => attribute.scale !== undefined)
.forEach((attribute: IStyleAttribute) => {
@ -121,9 +133,44 @@ export default class DataMappingPlugin implements ILayerPlugin {
// @ts-ignore
encodeRecord[attribute.name] =
Array.isArray(values) && values.length === 1 ? values[0] : values;
// 增加对 layer/text/iconfont unicode 映射的解析
if (attribute.name === 'shape') {
encodeRecord.shape = this.fontService.getIconFontKey(
encodeRecord[attribute.name] as string,
);
}
});
return encodeRecord;
}) as IEncodeFeature[];
// console.log('mappedData', mappedData[0])
// 根据地图的类型判断是否需要对点位数据进行处理, 若是高德2.0则需要对坐标进行相对偏移
if (mappedData.length > 0 && this.mapService.version === 'GAODE2.x') {
if (typeof mappedData[0].coordinates[0] === 'number') {
// 单个的点数据
// @ts-ignore
mappedData.map((d) => {
d.version = 'GAODE2.x';
// @ts-ignore
d.originCoordinates = cloneDeep(d.coordinates); // 为了兼容高德1.x 需要保存一份原始的经纬度坐标数据(许多上层逻辑依赖经纬度数据)
// @ts-ignore
d.coordinates = this.mapService.lngLatToCoord(d.coordinates);
// d.coordinates = this.mapService.lngLatToCoord(unProjectFlat(d.coordinates));
});
} else {
// 连续的线、面数据
// @ts-ignore
mappedData.map((d) => {
d.version = 'GAODE2.x';
// @ts-ignore
d.originCoordinates = cloneDeep(d.coordinates); // 为了兼容高德1.x 需要保存一份原始的经纬度坐标数据(许多上层逻辑依赖经纬度数据)
// @ts-ignore
d.coordinates = this.mapService.lngLatToCoords(d.coordinates);
});
}
}
return mappedData;
}
private applyAttributeMapping(

View File

@ -1,6 +1,13 @@
import { ILayer, ILayerPlugin, IMapService, TYPES } from '@antv/l7-core';
import {
ILayer,
ILayerPlugin,
ILngLat,
IMapService,
TYPES,
} from '@antv/l7-core';
import Source from '@antv/l7-source';
import { injectable } from 'inversify';
import { cloneDeep } from 'lodash';
@injectable()
export default class DataSourcePlugin implements ILayerPlugin {

View File

@ -5,6 +5,7 @@ import {
ICoordinateSystemService,
ILayer,
ILayerPlugin,
IMapService,
IRendererService,
TYPES,
} from '@antv/l7-core';
@ -29,13 +30,28 @@ export default class ShaderUniformPlugin implements ILayerPlugin {
@inject(TYPES.IRendererService)
private readonly rendererService: IRendererService;
@inject(TYPES.IMapService)
private readonly mapService: IMapService;
public apply(layer: ILayer) {
const version = this.mapService.version;
let mvp = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]; // default matrix (for gaode2.x)
let sceneCenterMKT = [0, 0];
layer.hooks.beforeRender.tap('ShaderUniformPlugin', () => {
// 重新计算坐标系参数
this.coordinateSystemService.refresh();
if (version === 'GAODE2.x') {
// @ts-ignore
mvp = this.mapService.map.customCoords.getMVPMatrix();
// mvp = amapCustomCoords.getMVPMatrix()
// @ts-ignore
sceneCenterMKT = this.mapService.getCustomCoordCenter();
}
const { width, height } = this.rendererService.getViewportSize();
layer.models.forEach((model) =>
layer.models.forEach((model) => {
model.addUniforms({
// 相机参数,包含 VP 矩阵、缩放等级
[CameraUniform.ProjectionMatrix]: this.cameraService.getProjectionMatrix(),
@ -52,13 +68,16 @@ export default class ShaderUniformPlugin implements ILayerPlugin {
[CoordinateUniform.PixelsPerDegree]: this.coordinateSystemService.getPixelsPerDegree(),
[CoordinateUniform.PixelsPerDegree2]: this.coordinateSystemService.getPixelsPerDegree2(),
[CoordinateUniform.PixelsPerMeter]: this.coordinateSystemService.getPixelsPerMeter(),
// 坐标系是高德2.0的时候单独计算
[CoordinateUniform.Mvp]: mvp,
u_SceneCenterMKT: sceneCenterMKT,
// 其他参数例如视口大小、DPR 等
u_ViewportSize: [width, height],
u_DevicePixelRatio: window.devicePixelRatio,
u_ModelMatrix: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1],
u_PickingBuffer: layer.getLayerConfig().pickingBuffer || 0,
}),
);
});
});
// TODO脏检查决定是否需要渲染
});

View File

@ -11,7 +11,6 @@ export default class PointLayer extends BaseLayer<IPointLayerStyleOptions> {
public buildModels() {
const modelType = this.getModelType();
this.layerModel = new PointModels[modelType](this);
this.models = this.layerModel.initModels();
}
public rebuildModels() {

View File

@ -108,7 +108,6 @@ export default class ImageModel extends BaseModel {
attributeIdx: number,
) => {
const iconMap = this.iconService.getIconMap();
const { shape } = feature;
const { x, y } = iconMap[shape as string] || { x: 0, y: 0 };
return [x, y];

View File

@ -292,7 +292,7 @@ export default class TextModel extends BaseModel {
return padBounds(bounds, 0.5);
}
/**
*
*
*/
private initTextFont() {
const {
@ -315,12 +315,39 @@ export default class TextModel extends BaseModel {
characterSet,
fontWeight,
fontFamily,
iconfont: false,
});
}
/**
*
* iconfont
*/
private generateGlyphLayout() {
private initIconFontTex() {
const {
fontWeight = '400',
fontFamily = 'sans-serif',
} = this.layer.getLayerConfig() as IPointTextLayerStyleOptions;
const data = this.layer.getEncodedData();
const characterSet: string[] = [];
data.forEach((item: IEncodeFeature) => {
let { shape = '' } = item;
shape = shape.toString();
if (characterSet.indexOf(shape) === -1) {
characterSet.push(shape);
}
});
this.fontService.setFontOptions({
characterSet,
fontWeight,
fontFamily,
iconfont: true,
});
}
/**
*
*/
private generateGlyphLayout(iconfont: boolean) {
// TODO:更新文字布局
const { mapping } = this.fontService;
const {
@ -330,7 +357,7 @@ export default class TextModel extends BaseModel {
} = this.layer.getLayerConfig() as IPointTextLayerStyleOptions;
const data = this.layer.getEncodedData();
this.glyphInfo = data.map((feature: IEncodeFeature) => {
const { shape = '', coordinates, id, size = 1 } = feature;
const { shape = '', id, size = 1 } = feature;
const shaping = shapeText(
shape.toString(),
@ -341,21 +368,31 @@ export default class TextModel extends BaseModel {
'center',
spacing,
textOffset,
iconfont,
);
const glyphQuads = getGlyphQuads(shaping, textOffset, false);
feature.shaping = shaping;
feature.glyphQuads = glyphQuads;
feature.centroid = calculteCentroid(coordinates);
// feature.centroid = calculteCentroid(coordinates);
feature.centroid = calculteCentroid(feature.coordinates);
// 此时地图高德2.0 originCentroid == centroid
feature.originCentroid =
feature.version === 'GAODE2.x'
? calculteCentroid(feature.originCoordinates)
: (feature.originCentroid = feature.centroid);
this.glyphInfoMap[id as number] = {
shaping,
glyphQuads,
centroid: calculteCentroid(coordinates),
centroid: calculteCentroid(feature.coordinates),
};
return feature;
});
}
/**
*
* depend on originCentorid
*/
private filterGlyphs() {
const {
@ -374,7 +411,11 @@ export default class TextModel extends BaseModel {
const collisionIndex = new CollisionIndex(width, height);
const filterData = this.glyphInfo.filter((feature: IEncodeFeature) => {
const { shaping, id = 0 } = feature;
const centroid = feature.centroid as [number, number];
// const centroid = feature.centroid as [number, number];
// const centroid = feature.originCentroid as [number, number];
const centroid = (feature.version === 'GAODE2.x'
? feature.originCentroid
: feature.centroid) as [number, number];
const size = feature.size as number;
const fontScale: number = size / 24;
const pixels = this.mapService.lngLatToContainer(centroid);
@ -404,10 +445,13 @@ export default class TextModel extends BaseModel {
*
*/
private initGlyph() {
// 1.生成文字纹理
this.initTextFont();
const { iconfont = false } = this.layer.getLayerConfig();
// 1.生成文字纹理(或是生成 iconfont
iconfont ? this.initIconFontTex() : this.initTextFont();
// this.initTextFont();
// 2.生成文字布局
this.generateGlyphLayout();
this.generateGlyphLayout(iconfont);
}
/**
*

View File

@ -11,6 +11,7 @@ attribute vec3 a_Size;
attribute vec3 a_Normal;
uniform mat4 u_ModelMatrix;
uniform mat4 u_Mvp;
uniform vec2 u_offsets;
varying vec4 v_color;
@ -30,6 +31,12 @@ void main() {
float lightWeight = calc_lighting(pos);
v_color =vec4(a_Color.rgb * lightWeight, a_Color.w);
// gl_Position = project_common_position_to_clipspace(pos);
if(u_CoordinateSystem == COORDINATE_SYSTEM_P20_2) { // gaode2.x
gl_Position = u_Mvp * pos;
} else {
gl_Position = project_common_position_to_clipspace(pos);
}
setPickingColor(a_PickingColor);
}

View File

@ -4,6 +4,7 @@ attribute vec2 a_Extrude;
attribute float a_Size;
attribute float a_Shape;
uniform mat4 u_ModelMatrix;
uniform mat4 u_Mvp;
uniform float u_stroke_width : 2;
uniform vec2 u_offsets;
@ -16,24 +17,16 @@ varying float v_radius;
#pragma include "picking"
void main() {
vec2 extrude = a_Extrude;
float shape_type = a_Shape;
float newSize = setPickingSize(a_Size);
// unpack color(vec2)
v_color = a_Color;
vec2 extrude = a_Extrude;
float shape_type = a_Shape;
float newSize = setPickingSize(a_Size);
// radius(16-bit)
v_radius = newSize;
vec2 offset = project_pixel(extrude * (newSize + u_stroke_width) + u_offsets);
vec4 project_pos = project_position(vec4(a_Position.xy, 0.0, 1.0));
// TODO: billboard
// anti-alias
float antialiasblur = 1.0 / u_DevicePixelRatio / (newSize + u_stroke_width);
@ -41,8 +34,17 @@ void main() {
// construct point coords
v_data = vec4(extrude, antialiasblur,shape_type);
setPickingColor(a_PickingColor);
gl_Position = project_common_position_to_clipspace(vec4(project_pos.xy + offset, 0.0, 1.0));
vec2 offset = project_pixel(extrude * (newSize + u_stroke_width) + u_offsets);
vec4 project_pos = project_position(vec4(a_Position.xy, 0.0, 1.0));
// gl_Position = project_common_position_to_clipspace(vec4(project_pos.xy + offset, project_pixel(setPickingOrder(0.0)), 1.0));
if(u_CoordinateSystem == COORDINATE_SYSTEM_P20_2) { // gaode2.x
gl_Position = u_Mvp * vec4(project_pos.xy + offset, 0.0, 1.0);
} else {
gl_Position = project_common_position_to_clipspace(vec4(project_pos.xy + offset, project_pixel(setPickingOrder(0.0)), 1.0));
}
// gl_Position = project_common_position_to_clipspace(vec4(project_pos.xy + offset, 0.0, 1.0));
setPickingColor(a_PickingColor);
}

View File

@ -6,6 +6,7 @@ attribute float a_Size;
varying vec4 v_color;
varying vec2 v_uv;
uniform mat4 u_ModelMatrix;
uniform mat4 u_Mvp;
uniform float u_stroke_width : 1;
uniform vec2 u_offsets;
varying float v_size;
@ -19,7 +20,14 @@ void main() {
vec4 project_pos = project_position(vec4(a_Position, 1.0));
v_size = a_Size;
vec2 offset = project_pixel(u_offsets);
// gl_Position = project_common_position_to_clipspace(vec4(vec2(project_pos.xy + offset),project_pos.z, 1.0));
if(u_CoordinateSystem == COORDINATE_SYSTEM_P20_2) { // gaode2.x
gl_Position = u_Mvp * vec4(vec2(project_pos.xy + offset),project_pos.z, 1.0);
} else {
gl_Position = project_common_position_to_clipspace(vec4(vec2(project_pos.xy + offset),project_pos.z, 1.0));
}
gl_PointSize = a_Size * 2.0 * u_DevicePixelRatio;
setPickingColor(a_PickingColor);

View File

@ -1,6 +1,7 @@
attribute vec3 a_Position;
uniform mat4 u_ModelMatrix;
uniform mat4 u_Mvp;
attribute float a_Size;
attribute vec4 a_Color;
varying vec4 v_color;
@ -11,9 +12,18 @@ uniform vec2 u_offsets;
#pragma include "picking"
void main() {
v_color = a_Color;
// vec2 offset = project_pixel(u_offsets);
// vec4 project_pos = project_position(vec4(a_Position, 1.0)) + vec4(a_Size / 2.,-a_Size /2.,0.,0.);
// gl_Position = project_common_position_to_clipspace(vec4(vec2(project_pos.xy+offset),project_pos.z,project_pos.w));\
//
if(u_CoordinateSystem == COORDINATE_SYSTEM_P20_2) { // gaode2.x
gl_Position = u_Mvp * vec4(a_Position, 1.0);
} else { // else
vec2 offset = project_pixel(u_offsets);
vec4 project_pos = project_position(vec4(a_Position, 1.0)) + vec4(a_Size / 2.,-a_Size /2.,0.,0.);
gl_Position = project_common_position_to_clipspace(vec4(vec2(project_pos.xy+offset),project_pos.z,project_pos.w));
}
gl_PointSize = a_Size * 2.0 * u_DevicePixelRatio;
setPickingColor(a_PickingColor);
}

View File

@ -10,6 +10,7 @@ attribute float a_Rotate;
uniform vec2 u_sdf_map_size;
uniform mat4 u_ModelMatrix;
uniform mat4 u_Mvp;
varying vec2 v_uv;
varying float v_gamma_scale;
@ -26,14 +27,24 @@ void main() {
// 文本缩放比例
float fontScale = a_Size / FONT_SIZE;
v_fontScale = fontScale;
vec4 project_pos = project_position(vec4(a_Position, 1.0));
vec4 projected_position = project_common_position_to_clipspace(vec4(project_pos.xyz, 1.0));
vec4 project_pos = project_position(vec4(a_Position, 1.0));
// vec4 projected_position = project_common_position_to_clipspace(vec4(project_pos.xyz, 1.0));
highp float angle_sin = sin(a_Rotate);
highp float angle_cos = cos(a_Rotate);
mat2 rotation_matrix = mat2(angle_cos, -1.0 * angle_sin, angle_sin, angle_cos);
gl_Position = vec4(projected_position.xy / projected_position.w
+ rotation_matrix * a_textOffsets * fontScale / u_ViewportSize * 2.0 * u_DevicePixelRatio, 0.0, 1.0);
// gl_Position = vec4(projected_position.xy / projected_position.w + rotation_matrix * a_textOffsets * fontScale / u_ViewportSize * 2.0 * u_DevicePixelRatio, 0.0, 1.0);
vec4 projected_position;
if(u_CoordinateSystem == COORDINATE_SYSTEM_P20_2) { // gaode2.x
projected_position = u_Mvp * (vec4(a_Position.xyz, 1.0));
} else { // else
projected_position = project_common_position_to_clipspace(vec4(project_pos.xyz, 1.0));
}
gl_Position = vec4(projected_position.xy / projected_position.w + rotation_matrix * a_textOffsets * fontScale / u_ViewportSize * 2.0 * u_DevicePixelRatio, 0.0, 1.0);
v_gamma_scale = gl_Position.w;
setPickingColor(a_PickingColor);

View File

@ -9,6 +9,7 @@ attribute vec3 a_Position;
attribute vec3 a_Normal;
attribute float a_Size;
uniform mat4 u_ModelMatrix;
uniform mat4 u_Mvp;
varying vec4 v_Color;
@ -20,7 +21,14 @@ void main() {
vec4 pos = vec4(a_Position.xy, a_Position.z * a_Size, 1.0);
vec4 project_pos = project_position(pos);
// gl_Position = project_common_position_to_clipspace(vec4(project_pos.xyz, 1.0));
if(u_CoordinateSystem == COORDINATE_SYSTEM_P20_2) { // gaode2.x
// gl_Position = u_Mvp * (vec4(project_pos.xyz * vec3(1.0, 1.0, -1.0), 1.0));
gl_Position = u_Mvp * (vec4(project_pos.xyz, 1.0));
} else {
gl_Position = project_common_position_to_clipspace(vec4(project_pos.xyz, 1.0));
}
float lightWeight = calc_lighting(pos);
// v_Color = a_Color;

View File

@ -3,6 +3,7 @@ attribute vec3 a_Position;
attribute vec3 a_Normal;
attribute float a_Size;
uniform mat4 u_ModelMatrix;
uniform mat4 u_Mvp;
varying vec4 v_Color;
@ -12,7 +13,13 @@ varying vec4 v_Color;
void main() {
v_Color = a_Color;
vec4 project_pos = project_position(vec4(a_Position, 1.0));
// gl_Position = project_common_position_to_clipspace(vec4(project_pos.xyz, 1.0));
if(u_CoordinateSystem == COORDINATE_SYSTEM_P20_2) { // gaode2.x
gl_Position = u_Mvp * (vec4(project_pos.xyz, 1.0));
} else {
gl_Position = project_common_position_to_clipspace(vec4(project_pos.xyz, 1.0));
}
setPickingColor(a_PickingColor);
}

View File

@ -29,7 +29,7 @@ interface IRasterLayerStyleOptions {
export default class RasterLayer extends BaseLayer<IRasterLayerStyleOptions> {
public type: string = 'RasterLayer';
protected texture: ITexture2D;
protected rasterTexture: ITexture2D;
protected colorTexture: ITexture2D;
public getAnimateUniforms(): IModelUniform {
@ -39,7 +39,7 @@ export default class RasterLayer extends BaseLayer<IRasterLayerStyleOptions> {
public buildModels() {
const parserDataItem = this.getSource().data.dataArray[0];
const { createTexture2D } = this.rendererService;
this.texture = createTexture2D({
this.rasterTexture = createTexture2D({
data: parserDataItem.data,
width: parserDataItem.width,
height: parserDataItem.height,
@ -65,7 +65,7 @@ export default class RasterLayer extends BaseLayer<IRasterLayerStyleOptions> {
model.draw({
uniforms: {
u_opacity: opacity || 1,
u_texture: this.texture,
u_texture: this.rasterTexture,
u_min: min,
u_width: width,
u_height: height,

View File

@ -13,7 +13,7 @@ interface IRasterLayerStyleOptions {
export default class Raster2dLayer extends BaseLayer<IRasterLayerStyleOptions> {
public type: string = 'RasterLayer';
protected texture: ITexture2D;
protected rasterTexture: ITexture2D;
protected colorTexture: ITexture2D;
public buildModels() {
@ -21,7 +21,7 @@ export default class Raster2dLayer extends BaseLayer<IRasterLayerStyleOptions> {
const source = this.getSource();
const { createTexture2D } = this.rendererService;
const parserDataItem = this.getSource().data.dataArray[0];
this.texture = createTexture2D({
this.rasterTexture = createTexture2D({
data: parserDataItem.data,
width: parserDataItem.width,
height: parserDataItem.height,
@ -61,12 +61,12 @@ export default class Raster2dLayer extends BaseLayer<IRasterLayerStyleOptions> {
const { opacity } = this.getLayerConfig();
const parserDataItem = this.getSource().data.dataArray[0];
const { min, max } = parserDataItem;
if (this.texture) {
if (this.rasterTexture) {
this.models.forEach((model) =>
model.draw({
uniforms: {
u_opacity: opacity || 1,
u_texture: this.texture,
u_texture: this.rasterTexture,
u_min: min,
u_max: max,
u_colorTexture: this.colorTexture,

View File

@ -1,5 +1,6 @@
precision highp float;
uniform mat4 u_ModelMatrix;
uniform mat4 u_Mvp;
attribute vec3 a_Position;
attribute vec2 a_Uv;
varying vec2 v_texCoord;
@ -8,4 +9,10 @@ void main() {
v_texCoord = a_Uv;
vec4 project_pos = project_position(vec4(a_Position, 1.0));
gl_Position = project_common_position_to_clipspace(vec4(project_pos.xy,0., 1.0));
if(u_CoordinateSystem == COORDINATE_SYSTEM_P20_2) { // gaode2.x
gl_Position = u_Mvp * (vec4(project_pos.xy,0., 1.0));
} else {
gl_Position = project_common_position_to_clipspace(vec4(project_pos.xy,0., 1.0));
}
}

View File

@ -1,6 +1,7 @@
precision highp float;
attribute vec3 a_Position;
uniform mat4 u_ModelMatrix;
uniform mat4 u_Mvp;
uniform vec4 u_extent;
uniform sampler2D u_texture;
uniform sampler2D u_colorTexture;
@ -32,6 +33,12 @@ void main() {
// vec2 range = u_extent.zw - u_extent.xy;
// vec4 project_pos = project_position(vec4(pos, 0, 1.0));
// gl_Position = project_common_position_to_clipspace(vec4(pos.xy, project_scale(value) * u_heightRatio, 1.0));
if(u_CoordinateSystem == COORDINATE_SYSTEM_P20_2) { // gaode2.x
gl_Position = u_Mvp * (vec4(pos.xy, project_scale(value) * u_heightRatio, 1.0));
} else {
gl_Position = project_common_position_to_clipspace(vec4(pos.xy, project_scale(value) * u_heightRatio, 1.0));
}
}

View File

@ -87,6 +87,51 @@ export default class ExtrudePolyline {
startIndex: 0,
};
}
public extrude_gaode2(points: number[][], originPoints: number[][]) {
const complex = this.complex;
if (points.length <= 1) {
return complex;
}
this.lastFlip = -1;
this.started = false;
this.normal = null;
this.totalDistance = 0;
// 去除数组里重复的点
// points = getArrayUnique(points);
const total = points.length;
let count = complex.startIndex;
for (let i = 1; i < total; i++) {
const last = points[i - 1] as vec2;
const originLast = originPoints[i - 1] as vec2;
const cur = points[i] as vec2;
const originCur = originPoints[i] as vec2;
const next = i < points.length - 1 ? points[i + 1] : null;
const originNext =
i < originPoints.length - 1 ? originPoints[i + 1] : null;
const amt = this.segment_gaode2(
complex,
count,
last,
cur,
next as vec2,
originLast,
originCur,
originNext as vec2,
);
count += amt;
}
if (this.dash) {
for (let i = 0; i < complex.positions.length / 6; i++) {
complex.positions[i * 6 + 5] = this.totalDistance;
}
}
complex.startIndex = complex.positions.length / 6;
return complex;
}
public extrude(points: number[][]) {
const complex = this.complex;
if (points.length <= 1) {
@ -115,6 +160,222 @@ export default class ExtrudePolyline {
complex.startIndex = complex.positions.length / 6;
return complex;
}
private segment_gaode2(
complex: any,
index: number,
last: vec2,
cur: vec2,
next: vec2,
originLast: vec2,
originCur: vec2,
originNext: vec2,
) {
let count = 0;
const indices = complex.indices;
const positions = complex.positions;
const normals = complex.normals;
const capSquare = this.cap === 'square';
const joinBevel = this.join === 'bevel';
const flatCur = aProjectFlat([originCur[0], originCur[1]]) as [
number,
number,
];
const flatLast = aProjectFlat([originLast[0], originLast[1]]) as [
number,
number,
];
direction(lineA, cur, last);
let segmentDistance = 0;
if (this.dash) {
segmentDistance = this.lineSegmentDistance(flatCur, flatLast);
this.totalDistance += segmentDistance;
}
if (!this.normal) {
this.normal = vec2.create();
computeNormal(this.normal, lineA);
}
if (!this.started) {
this.started = true;
// if the end cap is type square, we can just push the verts out a bit
if (capSquare) {
// vec2.scaleAndAdd(capEnd, last, lineA, -this.thickness);
const out1 = vec2.create();
const out2 = vec2.create();
vec2.add(out1, this.normal, lineA);
vec2.add(out2, this.normal, lineA);
normals.push(out2[0], out2[1], 0);
normals.push(out1[0], out1[1], 0);
positions.push(
last[0],
last[1],
0,
this.totalDistance - segmentDistance,
-this.thickness,
0,
);
positions.push(
last[0],
last[1],
0,
this.totalDistance - segmentDistance,
this.thickness,
0,
);
} else {
this.extrusions(
positions,
normals,
last,
this.normal,
this.thickness,
this.totalDistance - segmentDistance,
);
}
}
indices.push(index + 0, index + 1, index + 2);
if (!next) {
computeNormal(this.normal, lineA);
if (capSquare) {
const out1 = vec2.create();
const out2 = vec2.create();
vec2.sub(out2, lineA, this.normal);
vec2.add(out1, lineA, this.normal);
normals.push(out2[0], out2[1], 0);
normals.push(out1[0], out1[1], 0);
positions.push(
cur[0],
cur[1],
0,
this.totalDistance,
this.thickness,
0,
);
positions.push(
cur[0],
cur[1],
0,
this.totalDistance,
this.thickness,
0,
);
} else {
this.extrusions(
positions,
normals,
cur,
this.normal,
this.thickness,
this.totalDistance,
);
}
indices.push(
...(this.lastFlip === 1
? [index, index + 2, index + 3]
: [index + 2, index + 1, index + 3]),
);
count += 2;
} else {
if (isPointEqual(cur, next)) {
vec2.add(
next,
cur,
vec2.normalize(next, vec2.subtract(next, cur, last)),
);
}
direction(lineB, next, cur);
// stores tangent & miter
const [miterLen, miter] = computeMiter(
tangent,
vec2.create(),
lineA,
lineB,
this.thickness,
);
// normal(tmp, lineA)
// get orientation
let flip = vec2.dot(tangent, this.normal) < 0 ? -1 : 1;
let bevel = joinBevel;
if (!bevel && this.join === 'miter') {
const limit = miterLen;
if (limit > this.miterLimit) {
bevel = true;
}
}
if (bevel) {
normals.push(this.normal[0], this.normal[1], 0);
normals.push(miter[0], miter[1], 0);
positions.push(
cur[0],
cur[1],
0,
this.totalDistance,
-this.thickness * flip,
0,
);
positions.push(
cur[0],
cur[1],
0,
this.totalDistance,
this.thickness * flip,
0,
);
indices.push(
...(this.lastFlip !== -flip
? [index, index + 2, index + 3]
: [index + 2, index + 1, index + 3]),
);
// now add the bevel triangle
indices.push(index + 2, index + 3, index + 4);
computeNormal(tmp, lineB);
vec2.copy(this.normal, tmp); // store normal for next round
normals.push(this.normal[0], this.normal[1], 0);
positions.push(
cur[0],
cur[1],
0,
this.totalDistance,
-this.thickness * flip,
0,
);
count += 3;
} else {
this.extrusions(
positions,
normals,
cur,
miter,
miterLen,
this.totalDistance,
);
indices.push(
...(this.lastFlip === 1
? [index, index + 2, index + 3]
: [index + 2, index + 1, index + 3]),
);
flip = -1;
// the miter is now the normal for our next join
vec2.copy(this.normal, miter);
count += 2;
}
this.lastFlip = flip;
}
return count;
}
private segment(
complex: any,
index: number,
@ -329,7 +590,6 @@ export default class ExtrudePolyline {
}
return count;
}
private extrusions(
positions: number[],
normals: number[],

View File

@ -197,6 +197,81 @@ function shapeLines(
shaping.right = shaping.left + maxLineLength;
}
function shapeIconFont(
shaping: any,
glyphMap: any,
iconfonts: any[],
lineHeight: number,
textAnchor: anchorType,
textJustify: string,
spacing: number,
) {
// buffer 为 4
const yOffset = -8;
let x = 0;
let y = yOffset;
let maxLineLength = 0;
const positionedGlyphs = shaping.positionedGlyphs;
const justify =
textJustify === 'right' ? 1 : textJustify === 'left' ? 0 : 0.5;
const lineStartIndex = positionedGlyphs.length;
iconfonts.forEach((iconfont) => {
const glyph = glyphMap[iconfont];
const baselineOffset = 0;
if (glyph) {
positionedGlyphs.push({
glyph: iconfont,
x,
y: y + baselineOffset,
vertical: false, // TODO目前只支持水平方向
scale: 1,
metrics: glyph,
});
x += glyph.advance + spacing;
}
// 左右对齐
if (positionedGlyphs.length !== lineStartIndex) {
const lineLength = x - spacing;
maxLineLength = Math.max(lineLength, maxLineLength);
justifyLine(
positionedGlyphs,
glyphMap,
lineStartIndex,
positionedGlyphs.length - 1,
justify,
);
}
x = 0;
y += lineHeight;
});
const { horizontalAlign, verticalAlign } = getAnchorAlignment(textAnchor);
align(
positionedGlyphs,
justify,
horizontalAlign,
verticalAlign,
maxLineLength,
lineHeight,
iconfonts.length,
);
// 计算包围盒
const height = y - yOffset;
shaping.top += -verticalAlign * height;
shaping.bottom = shaping.top + height;
shaping.left += -horizontalAlign * maxLineLength;
shaping.right = shaping.left + maxLineLength;
}
/**
*
*
@ -207,6 +282,7 @@ function shapeLines(
* @param {string} textJustify
* @param {number} spacing
* @param {[number, number]} translate &
* @param {[boolean]} isIconFont iconfont
* @return {boolean|shaping}
*/
export function shapeText(
@ -217,6 +293,7 @@ export function shapeText(
textJustify: string,
spacing: number,
translate: [number, number] = [0, 0],
isIconFont: boolean,
) {
// TODO处理换行
const lines = text.split('\n');
@ -231,8 +308,17 @@ export function shapeText(
lineCount: lines.length,
text,
};
shapeLines(
isIconFont
? shapeIconFont(
shaping,
glyphs,
lines,
lineHeight,
textAnchor,
textJustify,
spacing,
)
: shapeLines(
shaping,
glyphs,
lines,

View File

@ -33,7 +33,6 @@ export default class Viewport implements IViewport {
// 计算透视投影矩阵 projectionMatrix
mat4.perspective(this.projectionMatrix, fov, aspect, near, far);
// 计算相机矩阵 viewMatrix
const eye = vec3.fromValues(
cameraHeight * Math.sin(pitchInRadians) * Math.sin(rotationInRadians),
@ -46,7 +45,6 @@ export default class Viewport implements IViewport {
Math.sin(pitchInRadians),
);
mat4.lookAt(this.viewMatrix, eye, vec3.fromValues(0, 0, 0), up);
this.viewUncenteredMatrix = mat4.clone(this.viewMatrix);
// 移动相机位置

View File

@ -25,6 +25,7 @@ import { mat4, vec2, vec3 } from 'gl-matrix';
import { inject, injectable } from 'inversify';
import { IAMapEvent, IAMapInstance } from '../../typings/index';
import { toPaddingOptions } from '../utils';
import { Version } from '../version';
import './logo.css';
import { MapTheme } from './theme';
import Viewport from './Viewport';
@ -54,6 +55,7 @@ const LNGLAT_OFFSET_ZOOM_THRESHOLD = 12; // 暂时关闭 fix 统一不同坐标
@injectable()
export default class AMapService
implements IMapService<AMap.Map & IAMapInstance> {
public version: string = Version['GAODE1.x'];
/**
*
*/
@ -328,7 +330,7 @@ export default class AMapService
} = this.config;
// 高德地图创建独立的container
// tslint:disable-next-line:typedef
await new Promise((resolve) => {
await new Promise<void>((resolve) => {
const resolveMap = () => {
if (mapInstance) {
this.map = mapInstance as AMap.Map & IAMapInstance;
@ -442,6 +444,10 @@ export default class AMapService
const { lng, lat } = this.getCenter();
if (this.cameraChangedCallback) {
// resync viewport
// console.log('cameraHeight', height)
// console.log('pitch', pitch)
// console.log('rotation', rotation)
// console.log('zoom', this.map.getZoom())
this.viewport.syncWithMapCamera({
aspect,
// AMap 定义 rotation 为顺时针方向,而 Mapbox 为逆时针
@ -458,7 +464,7 @@ export default class AMapService
offsetOrigin: [position.x, position.y],
});
const { offsetZoom = LNGLAT_OFFSET_ZOOM_THRESHOLD } = this.config;
// console.log('this.viewport', this.viewport)
// set coordinate system
if (this.viewport.getZoom() > offsetZoom) {
this.coordinateSystemService.setCoordinateSystem(

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