mirror of https://gitee.com/antv-l7/antv-l7
feat(component): add layer control
This commit is contained in:
parent
26c3cca511
commit
0e000f5715
|
@ -11,7 +11,7 @@ order: 1
|
|||
```javascript
|
||||
import {Scene} from '@l7/scene';
|
||||
const scene =new L7.Scene({
|
||||
id:'map'
|
||||
id:'map',
|
||||
mapStyle:'dark',
|
||||
center:[ 110.770672, 34.159869 ],
|
||||
pitch:45
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
import { Scene } from '@l7/scene';
|
||||
import { HeatMapGridLayer } from '@l7/layers';
|
||||
const scene = new Scene({
|
||||
id: 'map',
|
||||
style: 'light',
|
||||
pitch: 0,
|
||||
center: [116.49434030056, 39.868073421167621],
|
||||
type: 'amap',
|
||||
zoom: 16,
|
||||
});
|
||||
|
||||
fetch('https://gw.alipayobjects.com/os/basement_prod/c3f8bda2-081b-449d-aa9f-9413b779205b.json')
|
||||
.then((res) => res.json())
|
||||
.then((data) => {
|
||||
const layer =
|
||||
new HeatMapGridLayer({
|
||||
})
|
||||
.source(data, {
|
||||
parser: {
|
||||
type: 'json',
|
||||
x: 'lng',
|
||||
y: 'lat',
|
||||
},
|
||||
transforms: [
|
||||
{
|
||||
type: 'grid',
|
||||
size: 50,
|
||||
field: 'count',
|
||||
method: 'sum',
|
||||
},
|
||||
],
|
||||
})
|
||||
.size('sum', (value) => {
|
||||
return value;
|
||||
})
|
||||
.shape('square')
|
||||
.style({
|
||||
coverage: 0.8,
|
||||
angle: 0,
|
||||
opacity: 0.6,
|
||||
})
|
||||
.color('count', [
|
||||
'#002466',
|
||||
'#105CB3',
|
||||
'#2894E0',
|
||||
'#CFF6FF',
|
||||
'#FFF5B8',
|
||||
'#FFAB5C',
|
||||
'#F27049',
|
||||
'#730D1C',
|
||||
]);
|
||||
scene.addLayer(layer);
|
||||
|
||||
|
||||
});
|
|
@ -1,39 +0,0 @@
|
|||
import { Scene } from '@l7/scene';
|
||||
import { LineLayer } from '@l7/layers'
|
||||
const scene = new Scene({
|
||||
id: 'map',
|
||||
pitch: 0,
|
||||
type: 'mapbox',
|
||||
style: 'light',
|
||||
center: [102.602992, 23.107329],
|
||||
zoom: 13,
|
||||
});
|
||||
|
||||
fetch('https://gw.alipayobjects.com/os/rmsportal/ZVfOvhVCzwBkISNsuKCc.json')
|
||||
.then((res) => res.json())
|
||||
.then((data) => {
|
||||
const layer =
|
||||
new LineLayer({
|
||||
})
|
||||
.source(data)
|
||||
.size(1)
|
||||
.shape('line')
|
||||
.color(
|
||||
'ELEV',
|
||||
[
|
||||
'#E8FCFF',
|
||||
'#CFF6FF',
|
||||
'#A1E9ff',
|
||||
'#65CEF7',
|
||||
'#3CB1F0',
|
||||
'#2894E0',
|
||||
'#1772c2',
|
||||
'#105CB3',
|
||||
'#0D408C',
|
||||
'#002466',
|
||||
].reverse(),
|
||||
)
|
||||
scene.addLayer(layer);
|
||||
console.log(layer);
|
||||
|
||||
});
|
|
@ -1,13 +1,16 @@
|
|||
{
|
||||
"title": {
|
||||
"zh": "热力图",
|
||||
"zh": "网格热力图",
|
||||
"en": "heatmap"
|
||||
},
|
||||
"demos": [
|
||||
{
|
||||
"filename": "line.js",
|
||||
"title": "线图层",
|
||||
"screenshot": "https://gw.alipayobjects.com/mdn/antv_site/afts/img/A*KCyXTJrePiYAAAAAAAAAAABkARQnAQ"
|
||||
"filename": "grid.js",
|
||||
"title": "网格热力图"
|
||||
},
|
||||
{
|
||||
"filename": "world.js",
|
||||
"title": "世界电厂热力图"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
import { Scene } from '@l7/scene';
|
||||
import { HeatMapGridLayer } from '@l7/layers';
|
||||
const scene = new Scene({
|
||||
id: 'map',
|
||||
style: 'light',
|
||||
pitch: 0,
|
||||
center: [116.49434030056, 39.868073421167621],
|
||||
type: 'amap',
|
||||
zoom: 3,
|
||||
});
|
||||
|
||||
fetch('https://gw.alipayobjects.com/os/basement_prod/337ddbb7-aa3f-4679-ab60-d64359241955.json')
|
||||
.then((res) => res.json())
|
||||
.then((data) => {
|
||||
const layer =
|
||||
new HeatMapGridLayer({
|
||||
})
|
||||
.source(data, {
|
||||
transforms: [
|
||||
{
|
||||
type: 'grid',
|
||||
size: 100000,
|
||||
field: 'capacity',
|
||||
method: 'sum',
|
||||
},
|
||||
],
|
||||
})
|
||||
.size('sum', (value) => {
|
||||
return value;
|
||||
})
|
||||
.scale('sum',{
|
||||
type:'quantile',
|
||||
})
|
||||
.shape('square')
|
||||
.style({
|
||||
coverage: 1,
|
||||
angle: 0,
|
||||
opacity: 1.0,
|
||||
})
|
||||
.color('sum', [
|
||||
'#002466',
|
||||
'#105CB3',
|
||||
'#2894E0',
|
||||
'#CFF6FF',
|
||||
'#FFF5B8',
|
||||
'#FFAB5C',
|
||||
'#F27049',
|
||||
'#730D1C',
|
||||
]);
|
||||
scene.addLayer(layer);
|
||||
|
||||
|
||||
});
|
|
@ -1,4 +1,4 @@
|
|||
---
|
||||
title: 热力图
|
||||
order: 0
|
||||
title: 网格热力图
|
||||
order: 1
|
||||
---
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
import { Scene } from '@l7/scene';
|
||||
import { HeatMapGridLayer } from '@l7/layers';
|
||||
const scene = new Scene({
|
||||
id: 'map',
|
||||
style: 'light',
|
||||
pitch: 0,
|
||||
center: [116.49434030056, 39.868073421167621],
|
||||
type: 'amap',
|
||||
zoom: 16,
|
||||
});
|
||||
|
||||
fetch('https://gw.alipayobjects.com/os/basement_prod/c3f8bda2-081b-449d-aa9f-9413b779205b.json')
|
||||
.then((res) => res.json())
|
||||
.then((data) => {
|
||||
const layer =
|
||||
new HeatMapGridLayer({
|
||||
})
|
||||
.source(data, {
|
||||
parser: {
|
||||
type: 'json',
|
||||
x: 'lng',
|
||||
y: 'lat',
|
||||
},
|
||||
transforms: [
|
||||
{
|
||||
type: 'grid',
|
||||
size: 50,
|
||||
field: 'count',
|
||||
method: 'sum',
|
||||
},
|
||||
],
|
||||
})
|
||||
.size('sum', (value) => {
|
||||
return value;
|
||||
})
|
||||
.shape('square')
|
||||
.style({
|
||||
coverage: 0.8,
|
||||
angle: 0,
|
||||
opacity: 0.6,
|
||||
})
|
||||
.color('count', [
|
||||
'#002466',
|
||||
'#105CB3',
|
||||
'#2894E0',
|
||||
'#CFF6FF',
|
||||
'#FFF5B8',
|
||||
'#FFAB5C',
|
||||
'#F27049',
|
||||
'#730D1C',
|
||||
]);
|
||||
scene.addLayer(layer);
|
||||
|
||||
|
||||
});
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"title": {
|
||||
"zh": "网格热力图",
|
||||
"en": "heatmap"
|
||||
},
|
||||
"demos": [
|
||||
{
|
||||
"filename": "grid.js",
|
||||
"title": "网格热力图"
|
||||
},
|
||||
{
|
||||
"filename": "world.js",
|
||||
"title": "世界电厂热力图"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
import { Scene } from '@l7/scene';
|
||||
import { HeatMapLayer } from '@l7/layers';
|
||||
const scene = new Scene({
|
||||
id: 'map',
|
||||
style: 'dark',
|
||||
pitch: 0,
|
||||
center: [116.49434030056, 39.868073421167621],
|
||||
type: 'mapbox',
|
||||
zoom: 3,
|
||||
});
|
||||
|
||||
fetch('https://gw.alipayobjects.com/os/basement_prod/337ddbb7-aa3f-4679-ab60-d64359241955.json')
|
||||
.then((res) => res.json())
|
||||
.then((data) => {
|
||||
const layer =
|
||||
new HeatMapLayer({
|
||||
})
|
||||
.source(data)
|
||||
.size('capacity', [0, 1]) // weight映射通道
|
||||
.style({
|
||||
intensity: 10,
|
||||
radius: 5,
|
||||
opacity: 1.0,
|
||||
rampColors: {
|
||||
colors: [
|
||||
'#2E8AE6',
|
||||
'#69D1AB',
|
||||
'#DAF291',
|
||||
'#FFD591',
|
||||
'#FF7A45',
|
||||
'#CF1D49',
|
||||
],
|
||||
positions: [0,0.2, 0.4, 0.6, 0.8, 1.0],
|
||||
},
|
||||
});
|
||||
scene.addLayer(layer);
|
||||
|
||||
|
||||
});
|
|
@ -0,0 +1,4 @@
|
|||
---
|
||||
title: 热力图
|
||||
order: 0
|
||||
---
|
|
@ -0,0 +1,44 @@
|
|||
import { ArcLineLayer } from '@l7/layers';
|
||||
import { Scene } from '@l7/scene';
|
||||
const scene = new Scene({
|
||||
id: 'map',
|
||||
pitch: 0,
|
||||
type: 'mapbox',
|
||||
style: 'dark',
|
||||
center: [102.602992, 23.107329],
|
||||
zoom: 0,
|
||||
});
|
||||
|
||||
fetch('https://gw.alipayobjects.com/os/basement_prod/b83699f9-a96d-49b8-b2ea-f99299faebaf.json')
|
||||
.then((res) => res.json())
|
||||
.then((data) => {
|
||||
|
||||
function getAirportCoord(idx) {
|
||||
return [data.airports[idx][3], data.airports[idx][4]];
|
||||
}
|
||||
const routes = data.routes.map(function (airline) {
|
||||
return {
|
||||
coord: [
|
||||
getAirportCoord(airline[1]),
|
||||
getAirportCoord(airline[2])
|
||||
]
|
||||
}
|
||||
});
|
||||
|
||||
const layer =
|
||||
new ArcLineLayer({})
|
||||
.source(routes, {
|
||||
parser: {
|
||||
type: 'json',
|
||||
coordinates: 'coord',
|
||||
},
|
||||
})
|
||||
.size(0.6)
|
||||
.shape('arc')
|
||||
.color('rgb(5, 5, 50)')
|
||||
.style({
|
||||
opacity: 0.05,
|
||||
})
|
||||
;
|
||||
scene.addLayer(layer);
|
||||
})
|
|
@ -0,0 +1,34 @@
|
|||
import { Arc3DLineLayer } from '@l7/layers';
|
||||
import { Scene } from '@l7/scene';
|
||||
const scene = new Scene({
|
||||
id: 'map',
|
||||
pitch: 40,
|
||||
type: 'amap',
|
||||
style: 'dark',
|
||||
center: [102.602992, 23.107329],
|
||||
zoom: 3,
|
||||
});
|
||||
|
||||
fetch('https://gw.alipayobjects.com/os/rmsportal/UEXQMifxtkQlYfChpPwT.txt')
|
||||
.then((res) => res.text())
|
||||
.then((data) => {
|
||||
const layer =
|
||||
new Arc3DLineLayer({})
|
||||
.source(data, {
|
||||
parser: {
|
||||
type: 'csv',
|
||||
x: 'lng1',
|
||||
y: 'lat1',
|
||||
x1: 'lng2',
|
||||
y1: 'lat2',
|
||||
},
|
||||
})
|
||||
.size(1)
|
||||
.shape('arc')
|
||||
.color('#1558AC')
|
||||
.style({
|
||||
opacity: 0.8,
|
||||
})
|
||||
;
|
||||
scene.addLayer(layer);
|
||||
})
|
|
@ -0,0 +1,35 @@
|
|||
import { Arc2DLineLayer } from '@l7/layers';
|
||||
import { Scene } from '@l7/scene';
|
||||
const scene = new Scene({
|
||||
id: 'map',
|
||||
pitch: 0,
|
||||
type: 'amap',
|
||||
style: 'dark',
|
||||
center: [102.602992, 23.107329],
|
||||
zoom: 2,
|
||||
});
|
||||
|
||||
fetch('https://gw.alipayobjects.com/os/rmsportal/UEXQMifxtkQlYfChpPwT.txt')
|
||||
.then((res) => res.text())
|
||||
.then((data) => {
|
||||
const layer =
|
||||
new Arc2DLineLayer({})
|
||||
.source(data, {
|
||||
parser: {
|
||||
type: 'csv',
|
||||
x: 'lng1',
|
||||
y: 'lat1',
|
||||
x1: 'lng2',
|
||||
y1: 'lat2',
|
||||
},
|
||||
})
|
||||
.size(1)
|
||||
.shape('arc')
|
||||
.color('#113681')
|
||||
.style({
|
||||
opacity: 0.8,
|
||||
blur: 0.99
|
||||
})
|
||||
;
|
||||
scene.addLayer(layer);
|
||||
})
|
|
@ -0,0 +1,24 @@
|
|||
{
|
||||
"title": {
|
||||
"zh": "弧线",
|
||||
"en": "line"
|
||||
},
|
||||
"demos": [
|
||||
|
||||
{
|
||||
"filename": "arc3d.js",
|
||||
"title": "3D弧线"
|
||||
|
||||
},
|
||||
{
|
||||
"filename": "arcCircle.js",
|
||||
"title": "大圆弧线"
|
||||
|
||||
},
|
||||
{
|
||||
"filename": "arc.js",
|
||||
"title": "弧线"
|
||||
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
---
|
||||
title: 弧线
|
||||
order: 1
|
||||
---
|
|
@ -3,10 +3,10 @@ import { LineLayer } from '@l7/layers'
|
|||
const scene = new Scene({
|
||||
id: 'map',
|
||||
pitch: 0,
|
||||
type: 'mapbox',
|
||||
type: 'amap',
|
||||
style: 'light',
|
||||
center: [102.602992, 23.107329],
|
||||
zoom: 13,
|
||||
zoom: 14,
|
||||
});
|
||||
|
||||
fetch('https://gw.alipayobjects.com/os/rmsportal/ZVfOvhVCzwBkISNsuKCc.json')
|
||||
|
@ -16,22 +16,25 @@ fetch('https://gw.alipayobjects.com/os/rmsportal/ZVfOvhVCzwBkISNsuKCc.json')
|
|||
new LineLayer({
|
||||
})
|
||||
.source(data)
|
||||
.size(1)
|
||||
.size('ELEV', (h) => {
|
||||
return h % 50 === 0 ? 1.0 : 0.5;
|
||||
})
|
||||
.shape('line')
|
||||
.scale('ELEV', {
|
||||
type: 'quantize'
|
||||
})
|
||||
.color(
|
||||
'ELEV',
|
||||
[
|
||||
'#E8FCFF',
|
||||
'#CFF6FF',
|
||||
'#A1E9ff',
|
||||
'#65CEF7',
|
||||
'#3CB1F0',
|
||||
'#2894E0',
|
||||
'#1772c2',
|
||||
'#105CB3',
|
||||
'#0D408C',
|
||||
'#002466',
|
||||
].reverse(),
|
||||
[ '#E4682F',
|
||||
'#FF8752',
|
||||
'#FFA783',
|
||||
'#FFBEA8',
|
||||
'#FFDCD6',
|
||||
'#EEF3FF',
|
||||
'#C8D7F5',
|
||||
'#A5C1FC',
|
||||
'#7FA7F9',
|
||||
'#5F8AE5' ].reverse()
|
||||
)
|
||||
scene.addLayer(layer);
|
||||
console.log(layer);
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
import { Scene } from '@l7/scene';
|
||||
import { LineLayer } from '@l7/layers'
|
||||
const scene = new Scene({
|
||||
id: 'map',
|
||||
pitch: 0,
|
||||
type: 'amap',
|
||||
style: 'light',
|
||||
center: [102.602992, 23.107329],
|
||||
zoom: 4,
|
||||
});
|
||||
|
||||
fetch('https://gw.alipayobjects.com/os/basement_prod/9f6afbcd-3aec-4a26-bd4a-2276d3439e0d.json')
|
||||
.then((res) => res.json())
|
||||
.then((data) => {
|
||||
const layer =
|
||||
new LineLayer({
|
||||
})
|
||||
.source(data)
|
||||
.scale('value',{
|
||||
type: 'quantile'
|
||||
})
|
||||
.size('value', [0.5, 1, 1.5, 2])
|
||||
.shape('line')
|
||||
.color('value', ['#FFF2E8', '#FFCEA7', '#F0A66C', '#CC464B', '#8A191A'])
|
||||
scene.addLayer(layer);
|
||||
console.log(layer);
|
||||
|
||||
});
|
|
@ -4,10 +4,17 @@
|
|||
"en": "line"
|
||||
},
|
||||
"demos": [
|
||||
{
|
||||
"filename": "path.js",
|
||||
"title": "路径"
|
||||
},
|
||||
{
|
||||
"filename": "line.js",
|
||||
"title": "线图层",
|
||||
"screenshot": "https://gw.alipayobjects.com/mdn/antv_site/afts/img/A*KCyXTJrePiYAAAAAAAAAAABkARQnAQ"
|
||||
"title": "等高线"
|
||||
},
|
||||
{
|
||||
"filename": "line2.js",
|
||||
"title": "等值线"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
import { Scene } from '@l7/scene';
|
||||
import { LineLayer } from '@l7/layers'
|
||||
const scene = new Scene({
|
||||
id: 'map',
|
||||
pitch: 0,
|
||||
type: 'amap',
|
||||
style: 'light',
|
||||
center: [120.2336, 30.2002],
|
||||
zoom: 15,
|
||||
});
|
||||
|
||||
fetch('https://gw.alipayobjects.com/os/basement_prod/65e9cebb-8063-45e7-b18f-727da84e9908.json')
|
||||
.then((res) => res.json())
|
||||
.then((data) => {
|
||||
const layer =
|
||||
new LineLayer({
|
||||
})
|
||||
.source(data)
|
||||
.size(1.5)
|
||||
.shape('line')
|
||||
.color(
|
||||
'name',
|
||||
['#5B8FF9','#5CCEA1','#7B320A' ]
|
||||
)
|
||||
scene.addLayer(layer);
|
||||
console.log(layer);
|
||||
|
||||
});
|
|
@ -1,4 +1,4 @@
|
|||
---
|
||||
title: 线图层
|
||||
order: 1
|
||||
title: 路径
|
||||
order: 0
|
||||
---
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
import { Scene } from '@l7/scene';
|
||||
import { DashLineLayer } from '@l7/layers'
|
||||
const scene = new Scene({
|
||||
id: 'map',
|
||||
pitch: 0,
|
||||
type: 'amap',
|
||||
style: 'light',
|
||||
center: [102.602992, 23.107329],
|
||||
zoom: 14,
|
||||
});
|
||||
|
||||
fetch('https://gw.alipayobjects.com/os/rmsportal/ZVfOvhVCzwBkISNsuKCc.json')
|
||||
.then((res) => res.json())
|
||||
.then((data) => {
|
||||
const layer =
|
||||
new DashLineLayer({
|
||||
})
|
||||
.source(data)
|
||||
.size('ELEV', (h) => {
|
||||
return h % 50 === 0 ? 1.0 : 0.5;
|
||||
})
|
||||
.shape('line')
|
||||
.scale('ELEV', {
|
||||
type: 'quantize'
|
||||
})
|
||||
.color(
|
||||
'ELEV',
|
||||
[ '#E4682F',
|
||||
'#FF8752',
|
||||
'#FFA783',
|
||||
'#FFBEA8',
|
||||
'#FFDCD6',
|
||||
'#EEF3FF',
|
||||
'#C8D7F5',
|
||||
'#A5C1FC',
|
||||
'#7FA7F9',
|
||||
'#5F8AE5' ].reverse()
|
||||
).style({
|
||||
dashArray:[10, 1],
|
||||
})
|
||||
scene.addLayer(layer);
|
||||
console.log(layer);
|
||||
|
||||
});
|
|
@ -0,0 +1,28 @@
|
|||
import { Scene } from '@l7/scene';
|
||||
import { DashLineLayer } from '@l7/layers'
|
||||
const scene = new Scene({
|
||||
id: 'map',
|
||||
pitch: 0,
|
||||
type: 'amap',
|
||||
style: 'light',
|
||||
center: [102.602992, 23.107329],
|
||||
zoom: 4,
|
||||
});
|
||||
|
||||
fetch('https://gw.alipayobjects.com/os/basement_prod/9f6afbcd-3aec-4a26-bd4a-2276d3439e0d.json')
|
||||
.then((res) => res.json())
|
||||
.then((data) => {
|
||||
const layer =
|
||||
new DashLineLayer({
|
||||
})
|
||||
.source(data)
|
||||
.scale('value',{
|
||||
type: 'quantile'
|
||||
})
|
||||
.size('value', [0.5, 1, 1.5, 2])
|
||||
.shape('line')
|
||||
.color('value', ['#FFF2E8', '#FFCEA7', '#F0A66C', '#CC464B', '#8A191A'])
|
||||
scene.addLayer(layer);
|
||||
console.log(layer);
|
||||
|
||||
});
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"title": {
|
||||
"zh": "线图层",
|
||||
"en": "line"
|
||||
},
|
||||
"demos": [
|
||||
{
|
||||
"filename": "path.js",
|
||||
"title": "路径"
|
||||
},
|
||||
{
|
||||
"filename": "line.js",
|
||||
"title": "等高线"
|
||||
},
|
||||
{
|
||||
"filename": "line2.js",
|
||||
"title": "等值线"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,96 @@
|
|||
import { Scene } from '@l7/scene';
|
||||
import { DashLineLayer } from '@l7/layers'
|
||||
const scene = new Scene({
|
||||
id: 'map',
|
||||
pitch: 0,
|
||||
type: 'amap',
|
||||
style: 'light',
|
||||
center: [120.2336, 30.2002],
|
||||
zoom: 3,
|
||||
});
|
||||
const lineData ={
|
||||
"type": "FeatureCollection",
|
||||
"features": [
|
||||
{
|
||||
"type": "Feature",
|
||||
"properties": {},
|
||||
"geometry": {
|
||||
"type": "LineString",
|
||||
"coordinates": [
|
||||
[
|
||||
102.98583984374999,
|
||||
37.666429212090605
|
||||
],
|
||||
[
|
||||
111.33544921874999,
|
||||
37.23032838760387
|
||||
],
|
||||
[
|
||||
111.24755859375,
|
||||
34.92197103616377
|
||||
],
|
||||
[
|
||||
98.15185546874999,
|
||||
35.44277092585766
|
||||
],
|
||||
[
|
||||
98.701171875,
|
||||
41.09591205639546
|
||||
],
|
||||
[
|
||||
100.5908203125,
|
||||
41.0130657870063
|
||||
],
|
||||
[
|
||||
101.09619140625,
|
||||
41.0130657870063
|
||||
],
|
||||
[
|
||||
101.689453125,
|
||||
41.0130657870063
|
||||
],
|
||||
[
|
||||
102.26074218749999,
|
||||
41.0130657870063
|
||||
],
|
||||
[
|
||||
102.26074218749999,
|
||||
40.58058466412761
|
||||
],
|
||||
[
|
||||
102.23876953125,
|
||||
40.329795743702064
|
||||
],
|
||||
[
|
||||
102.23876953125,
|
||||
39.977120098439634
|
||||
],
|
||||
[
|
||||
102.26074218749999,
|
||||
40.212440718286466
|
||||
],
|
||||
[
|
||||
102.48046875,
|
||||
39.87601941962116
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
fetch('https://gw.alipayobjects.com/os/basement_prod/65e9cebb-8063-45e7-b18f-727da84e9908.json')
|
||||
.then((res) => res.json())
|
||||
.then((data) => {
|
||||
const layer =
|
||||
new DashLineLayer({
|
||||
})
|
||||
.source(lineData)
|
||||
.size(1.5)
|
||||
.shape('line')
|
||||
.color(
|
||||
'#5B8FF9'
|
||||
)
|
||||
scene.addLayer(layer);
|
||||
console.log(layer);
|
||||
|
||||
});
|
|
@ -0,0 +1,4 @@
|
|||
---
|
||||
title: 虚线
|
||||
order: 0
|
||||
---
|
|
@ -1,5 +1,5 @@
|
|||
import { Scene } from '@l7/scene';
|
||||
import { PointLayer } from '@l7/layers'
|
||||
import { PointLayer, PointImageLayer } from '@l7/layers'
|
||||
const scene = new Scene({
|
||||
id: 'map',
|
||||
pitch: 0,
|
||||
|
@ -7,6 +7,7 @@ const scene = new Scene({
|
|||
style: 'light',
|
||||
center: [121.40, 31.258134],
|
||||
zoom: 15,
|
||||
minZoom: 10
|
||||
});
|
||||
|
||||
fetch('https://gw.alipayobjects.com/os/basement_prod/893d1d5f-11d9-45f3-8322-ee9140d288ae.json')
|
||||
|
@ -31,7 +32,7 @@ fetch('https://gw.alipayobjects.com/os/basement_prod/893d1d5f-11d9-45f3-8322-ee9
|
|||
|
||||
})
|
||||
|
||||
scene.addLayer(pointLayer);
|
||||
scene.addLayer(pointLayer);
|
||||
|
||||
});
|
||||
|
||||
|
|
|
@ -6,8 +6,13 @@
|
|||
"demos": [
|
||||
{
|
||||
"filename": "point.js",
|
||||
"title": "气泡图",
|
||||
"screenshot": "https://gw.alipayobjects.com/mdn/antv_site/afts/img/A*KCyXTJrePiYAAAAAAAAAAABkARQnAQ"
|
||||
"title": "气泡图"
|
||||
|
||||
},
|
||||
{
|
||||
"filename": "world.js",
|
||||
"title": "气泡图 - 电厂装机量"
|
||||
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ const scene = new Scene({
|
|||
style: 'light',
|
||||
center: [121.40, 31.258134],
|
||||
zoom: 15,
|
||||
|
||||
});
|
||||
|
||||
fetch('https://gw.alipayobjects.com/os/basement_prod/893d1d5f-11d9-45f3-8322-ee9140d288ae.json')
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
import { Scene } from '@l7/scene';
|
||||
import { PointLayer, PointImageLayer } from '@l7/layers'
|
||||
const scene = new Scene({
|
||||
id: 'map',
|
||||
pitch: 0,
|
||||
type: 'mapbox',
|
||||
style: 'dark',
|
||||
center: [121.40, 31.258134],
|
||||
zoom: 1,
|
||||
maxZoom: 10
|
||||
});
|
||||
|
||||
fetch('https://gw.alipayobjects.com/os/basement_prod/337ddbb7-aa3f-4679-ab60-d64359241955.json')
|
||||
.then((res) => res.json())
|
||||
.then((data) => {
|
||||
const pointLayer =
|
||||
new PointLayer({
|
||||
})
|
||||
.source(data).shape('circle')
|
||||
.size('capacity', [0, 20])
|
||||
.color('status', ['#ced1cc','#ffc83e','#ff8767','#dd54b6','#a45edb'])
|
||||
.style({
|
||||
opacity: 0.3,
|
||||
strokeWidth: 1,
|
||||
|
||||
})
|
||||
|
||||
scene.addLayer(pointLayer);
|
||||
|
||||
});
|
||||
|
||||
|
|
@ -34,7 +34,7 @@ fetch('https://gw.alipayobjects.com/os/basement_prod/893d1d5f-11d9-45f3-8322-ee9
|
|||
}
|
||||
})
|
||||
.shape('name', ['00', '01','02'])
|
||||
.size(60);
|
||||
.size(20);
|
||||
scene.addLayer(imageLayer);
|
||||
});
|
||||
|
||||
|
|
|
@ -3,39 +3,24 @@ import { PolygonLayer } from '@l7/layers'
|
|||
const scene = new Scene({
|
||||
id: 'map',
|
||||
pitch: 0,
|
||||
type: 'mapbox',
|
||||
style: 'dark',
|
||||
type: 'amap',
|
||||
style: 'light',
|
||||
center: [121.40, 31.258134],
|
||||
zoom: 3,
|
||||
});
|
||||
|
||||
fetch('https://gw.alipayobjects.com/os/basement_prod/d2e0e930-fd44-4fca-8872-c1037b0fee7b.json')
|
||||
fetch('https://gw.alipayobjects.com/os/rmsportal/JToMOWvicvJOISZFCkEI.json')
|
||||
.then((res) => res.json())
|
||||
.then((data) => {
|
||||
var colors = ["#D7F9F0", "#A6E1E0", "#72BED6", "#5B8FF9", "#3474DB", "#005CBE",'#00419F','#00287E'];
|
||||
const layer =
|
||||
new PolygonLayer({
|
||||
enablePicking: true,
|
||||
enableHighlight: true,
|
||||
highlightColor: 'red',
|
||||
onHover: (pickedFeature) => {
|
||||
// tslint:disable-next-line:no-console
|
||||
console.log(pickedFeature);
|
||||
},
|
||||
})
|
||||
.source(data)
|
||||
.color('name', [
|
||||
'#2E8AE6',
|
||||
'#69D1AB',
|
||||
'#DAF291',
|
||||
'#FFD591',
|
||||
'#FF7A45',
|
||||
'#CF1D49',
|
||||
])
|
||||
.shape('fill')
|
||||
.color('name', colors).shape('fill')
|
||||
.style({
|
||||
opacity: 1.0,
|
||||
});
|
||||
opacity: 0.9
|
||||
}).render();
|
||||
scene.addLayer(layer);
|
||||
console.log(layer);
|
||||
});
|
||||
|
||||
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
import { Scene } from '@l7/scene';
|
||||
import { PointLayer } from '@l7/layers'
|
||||
import { Scale, Zoom, Layers } from '@l7/component';
|
||||
const scene = new Scene({
|
||||
id: 'map',
|
||||
pitch: 0,
|
||||
|
@ -7,3 +9,49 @@ const scene = new Scene({
|
|||
center: [121.40, 31.258134],
|
||||
zoom: 5,
|
||||
});
|
||||
|
||||
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('circle')
|
||||
.size('unit_price', [5, 25])
|
||||
.color('name',['#49B5AD', "#5B8FF9"])
|
||||
.style({
|
||||
opacity: 0.3,
|
||||
strokeWidth: 1,
|
||||
})
|
||||
|
||||
scene.addLayer(pointLayer);
|
||||
var overlayers = {
|
||||
"围栏填充": pointLayer,
|
||||
};
|
||||
var baseLayers = {
|
||||
"基础地图": pointLayer,
|
||||
};
|
||||
const layersControl = new Layers({
|
||||
overlayers: overlayers,
|
||||
baseLayers,
|
||||
});
|
||||
|
||||
scene.addControl(layersControl);
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
const zoomControl = new Zoom();
|
||||
const scaleControl = new Scale();
|
||||
|
||||
scene.addControl(zoomControl);
|
||||
scene.addControl(scaleControl);
|
||||
|
||||
|
|
|
@ -1,10 +1,17 @@
|
|||
import { Scene } from '@l7/scene';
|
||||
import { Scale, Zoom } from '@l7/component';
|
||||
const scene = new Scene({
|
||||
id: 'map',
|
||||
pitch: 0,
|
||||
type: 'mapbox',
|
||||
style: 'light',
|
||||
center: [ -97.119140625, 38.75408327579141],
|
||||
center: [-97.119140625, 38.75408327579141],
|
||||
zoom: 2,
|
||||
});
|
||||
|
||||
const zoomControl = new Zoom();
|
||||
const scaleControl = new Scale();
|
||||
scene.addControl(zoomControl);
|
||||
scene.addControl(scaleControl);
|
||||
|
||||
|
||||
|
|
|
@ -6,13 +6,11 @@
|
|||
"demos": [
|
||||
{
|
||||
"filename": "amap.js",
|
||||
"title": "高德底图",
|
||||
"screenshot": "https://gw.alipayobjects.com/mdn/antv_site/afts/img/A*KCyXTJrePiYAAAAAAAAAAABkARQnAQ"
|
||||
"title": "高德底图组件",
|
||||
},
|
||||
{
|
||||
"filename": "mapbox.js",
|
||||
"title": "MapBox底图",
|
||||
"screenshot": "https://gw.alipayobjects.com/mdn/antv_site/afts/img/A*KCyXTJrePiYAAAAAAAAAAABkARQnAQ"
|
||||
"title": "MapBox底图组件"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
title: 组件
|
||||
order: 0
|
||||
---
|
||||
|
||||
|
|
@ -1,3 +1,5 @@
|
|||
import './packages/component/src/css/l7.css';
|
||||
window.scene = require('@l7/scene');
|
||||
window.layers= require('@l7/layers');
|
||||
window.component= require('@l7/component');
|
||||
|
||||
|
|
|
@ -1,6 +1,14 @@
|
|||
import { IControlService, IMapService, lazyInject, TYPES } from '@l7/core';
|
||||
import {
|
||||
IControlService,
|
||||
ILayerService,
|
||||
IMapService,
|
||||
IRendererService,
|
||||
lazyInject,
|
||||
TYPES,
|
||||
} from '@l7/core';
|
||||
import { DOM } from '@l7/utils';
|
||||
import { EventEmitter } from 'eventemitter3';
|
||||
import { inject } from 'inversify';
|
||||
|
||||
export enum PositionType {
|
||||
'TOPRIGHT' = 'topright',
|
||||
|
@ -20,9 +28,15 @@ export interface IControlOption {
|
|||
export default class Control extends EventEmitter {
|
||||
public controlOption: IControlOption;
|
||||
protected mapsService: IMapService;
|
||||
protected container: HTMLElement;
|
||||
|
||||
@lazyInject(TYPES.IRendererService)
|
||||
protected readonly renderService: IRendererService;
|
||||
@lazyInject(TYPES.ILayerService)
|
||||
protected readonly layerService: ILayerService;
|
||||
@lazyInject(TYPES.IControlService)
|
||||
private readonly controlService: IControlService;
|
||||
private container: HTMLElement;
|
||||
|
||||
private isShow: boolean;
|
||||
|
||||
constructor(cfg?: Partial<IControlOption>) {
|
||||
|
|
|
@ -10,12 +10,22 @@ export interface ILayerControlOption extends IControlOption {
|
|||
autoZIndex: boolean;
|
||||
hideSingleBase: boolean;
|
||||
sortLayers: boolean;
|
||||
|
||||
sortFunction: (...args: any[]) => any;
|
||||
}
|
||||
interface IInputItem extends HTMLInputElement {
|
||||
layerId: string;
|
||||
}
|
||||
export default class Layers extends Control {
|
||||
private layerControlInputs: any[];
|
||||
private layers: any[];
|
||||
private lastZIndex: number;
|
||||
private handlingClick: boolean;
|
||||
private layersLink: HTMLElement;
|
||||
private baseLayersList: HTMLElement;
|
||||
private separator: HTMLElement;
|
||||
private overlaysList: HTMLElement;
|
||||
private form: HTMLElement;
|
||||
|
||||
constructor(cfg: Partial<ILayerControlOption>) {
|
||||
super(cfg);
|
||||
|
@ -23,16 +33,25 @@ export default class Layers extends Control {
|
|||
this.layers = [];
|
||||
this.lastZIndex = 0;
|
||||
this.handlingClick = false;
|
||||
// const baseLayers = this.get('baseLayers');
|
||||
// const overlays = this.get('overlayers');
|
||||
// for (const i in baseLayers) {
|
||||
// this._addLayer(baseLayers[i], i);
|
||||
// }
|
||||
const { baseLayers = {}, overlayers = {} } = this.controlOption;
|
||||
|
||||
// for (const i in overlays) {
|
||||
// this._addLayer(overlays[i], i, true);
|
||||
// }
|
||||
// bindAll([ '_checkDisabledLayers', '_onLayerChange', 'collapse', 'extend', 'expand', '_onInputClick' ], this);
|
||||
Object.keys(baseLayers).forEach((name: string, index: number) => {
|
||||
this.addLayer(baseLayers[name], name, false);
|
||||
});
|
||||
Object.keys(overlayers).forEach((name: any, index: number) => {
|
||||
this.addLayer(overlayers[name], name, true);
|
||||
});
|
||||
bindAll(
|
||||
[
|
||||
'checkDisabledLayers',
|
||||
'onLayerChange',
|
||||
'collapse',
|
||||
'extend',
|
||||
'expand',
|
||||
'onInputClick',
|
||||
],
|
||||
this,
|
||||
);
|
||||
}
|
||||
|
||||
public getDefault() {
|
||||
|
@ -44,4 +63,248 @@ export default class Layers extends Control {
|
|||
sortLayers: false,
|
||||
};
|
||||
}
|
||||
public onAdd(MapService: IMapService) {
|
||||
this.initLayout();
|
||||
this.update();
|
||||
this.mapsService.on('zoomend', this.checkDisabledLayers);
|
||||
this.layers.forEach((layerItem) => {
|
||||
layerItem.layer.on('remove', this.onLayerChange);
|
||||
layerItem.layer.on('add', this.onLayerChange);
|
||||
});
|
||||
return this.container;
|
||||
}
|
||||
|
||||
public addVisualLayer(layer: any, name: string | number) {
|
||||
this.addLayer(layer, name, true);
|
||||
return this.mapsService ? this.update() : this;
|
||||
}
|
||||
public expand() {
|
||||
const { height } = this.renderService.getViewportSize();
|
||||
DOM.addClass(this.container, 'l7-control-layers-expanded');
|
||||
this.form.style.height = 'null';
|
||||
const acceptableHeight = height - (this.container.offsetTop + 50);
|
||||
if (acceptableHeight < this.form.clientHeight) {
|
||||
DOM.addClass(this.form, 'l7-control-layers-scrollbar');
|
||||
this.form.style.height = acceptableHeight + 'px';
|
||||
} else {
|
||||
DOM.removeClass(this.form, 'l7-control-layers-scrollbar');
|
||||
}
|
||||
this.checkDisabledLayers();
|
||||
return this;
|
||||
}
|
||||
|
||||
public collapse() {
|
||||
DOM.removeClass(this.container, 'l7-control-layers-expanded');
|
||||
return this;
|
||||
}
|
||||
|
||||
private initLayout() {
|
||||
const className = 'l7-control-layers';
|
||||
const container = (this.container = DOM.create('div', className));
|
||||
const { collapsed } = this.controlOption;
|
||||
|
||||
// makes this work on IE touch devices by stopping it from firing a mouseout event when the touch is released
|
||||
container.setAttribute('aria-haspopup', 'true');
|
||||
|
||||
const form = (this.form = DOM.create(
|
||||
'form',
|
||||
className + '-list',
|
||||
) as HTMLElement);
|
||||
|
||||
if (collapsed) {
|
||||
this.mapsService.on('click', this.collapse);
|
||||
container.addEventListener('mouseenter', this.expand);
|
||||
container.addEventListener('mouseleave', this.collapse);
|
||||
}
|
||||
|
||||
this.layersLink = DOM.create('a', className + '-toggle', container);
|
||||
const link = this.layersLink;
|
||||
// link.href = '#';
|
||||
link.title = 'Layers';
|
||||
if (!collapsed) {
|
||||
this.expand();
|
||||
}
|
||||
|
||||
this.baseLayersList = DOM.create('div', className + '-base', form);
|
||||
this.separator = DOM.create('div', className + '-separator', form);
|
||||
this.overlaysList = DOM.create('div', className + '-overlays', form);
|
||||
container.appendChild(form);
|
||||
}
|
||||
|
||||
private update() {
|
||||
if (!this.container) {
|
||||
return this;
|
||||
}
|
||||
|
||||
DOM.empty(this.baseLayersList);
|
||||
DOM.empty(this.overlaysList);
|
||||
|
||||
this.layerControlInputs = [];
|
||||
let baseLayersPresent;
|
||||
let overlaysPresent;
|
||||
let i;
|
||||
let obj;
|
||||
let baseLayersCount = 0;
|
||||
|
||||
for (i = 0; i < this.layers.length; i++) {
|
||||
obj = this.layers[i];
|
||||
this.addItem(obj);
|
||||
overlaysPresent = overlaysPresent || obj.overlay;
|
||||
baseLayersPresent = baseLayersPresent || !obj.overlay;
|
||||
baseLayersCount += !obj.overlay ? 1 : 0;
|
||||
}
|
||||
|
||||
// Hide base layers section if there's only one layer.
|
||||
if (this.controlOption.hideSingleBase) {
|
||||
baseLayersPresent = baseLayersPresent && baseLayersCount > 1;
|
||||
this.baseLayersList.style.display = baseLayersPresent ? '' : 'none';
|
||||
}
|
||||
|
||||
this.separator.style.display =
|
||||
overlaysPresent && baseLayersPresent ? '' : 'none';
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
private checkDisabledLayers() {
|
||||
const inputs = this.layerControlInputs;
|
||||
let input: IInputItem;
|
||||
let layer;
|
||||
const zoom = this.mapsService.getZoom();
|
||||
|
||||
for (let i = inputs.length - 1; i >= 0; i--) {
|
||||
input = inputs[i];
|
||||
layer = this.layerService.getLayer(input.layerId);
|
||||
if (layer) {
|
||||
input.disabled = layer.visible && !layer.isVisible();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private addLayer(layer: any, name: string | number, overlay: boolean) {
|
||||
if (this.mapsService) {
|
||||
layer.on('add', this.onLayerChange);
|
||||
layer.on('remove', this.onLayerChange);
|
||||
}
|
||||
this.layers.push({
|
||||
layer,
|
||||
name,
|
||||
overlay,
|
||||
});
|
||||
const { sortLayers, sortFunction, autoZIndex } = this.controlOption;
|
||||
if (sortLayers) {
|
||||
this.layers.sort((a, b) => {
|
||||
return sortFunction(a.layer, b.layer, a.name, b.name);
|
||||
});
|
||||
}
|
||||
|
||||
if (autoZIndex && layer.setZIndex) {
|
||||
this.lastZIndex++;
|
||||
layer.setZIndex(this.lastZIndex);
|
||||
}
|
||||
|
||||
this.expandIfNotCollapsed();
|
||||
}
|
||||
|
||||
private expandIfNotCollapsed() {
|
||||
if (this.mapsService && !this.controlOption.collapsed) {
|
||||
this.expand();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
private onLayerChange(e: any) {
|
||||
if (!this.handlingClick) {
|
||||
this.update();
|
||||
}
|
||||
|
||||
const obj = this.layerService.getLayer(e.target.layerId);
|
||||
|
||||
const type = obj.overlay
|
||||
? e.type === 'add'
|
||||
? 'overlayadd'
|
||||
: 'overlayremove'
|
||||
: e.type === 'add'
|
||||
? 'baselayerchange'
|
||||
: null;
|
||||
|
||||
if (type) {
|
||||
this.emit(type, obj); // TODO:图
|
||||
}
|
||||
}
|
||||
|
||||
private createRadioElement(name: string, checked: boolean): ChildNode {
|
||||
const radioHtml =
|
||||
'<input type="radio" class="l7-control-layers-selector" name="' +
|
||||
name +
|
||||
'"' +
|
||||
(checked ? ' checked="checked"' : '') +
|
||||
'/>';
|
||||
|
||||
const radioFragment = document.createElement('div');
|
||||
radioFragment.innerHTML = radioHtml;
|
||||
|
||||
return radioFragment.firstChild as ChildNode;
|
||||
}
|
||||
|
||||
private addItem(obj: any) {
|
||||
const label = document.createElement('label');
|
||||
const checked =
|
||||
this.layerService.getLayer(obj.layer.id) && obj.layer.isVisible();
|
||||
let input: IInputItem;
|
||||
|
||||
if (obj.overlay) {
|
||||
input = document.createElement('input') as IInputItem;
|
||||
input.type = 'checkbox';
|
||||
input.className = 'l7-control-layers-selector';
|
||||
input.defaultChecked = checked;
|
||||
} else {
|
||||
input = this.createRadioElement('l7-base-layers', checked) as IInputItem;
|
||||
}
|
||||
this.layerControlInputs.push(input);
|
||||
input.layerId = obj.layer.id;
|
||||
input.addEventListener('click', this.onInputClick);
|
||||
|
||||
const name = document.createElement('span');
|
||||
name.innerHTML = ' ' + obj.name;
|
||||
|
||||
const holder = document.createElement('div');
|
||||
|
||||
label.appendChild(holder);
|
||||
holder.appendChild(input);
|
||||
holder.appendChild(name);
|
||||
|
||||
const container = obj.overlay ? this.overlaysList : this.baseLayersList;
|
||||
container.appendChild(label);
|
||||
|
||||
this.checkDisabledLayers();
|
||||
return label;
|
||||
}
|
||||
|
||||
private onInputClick() {
|
||||
const inputs = this.layerControlInputs;
|
||||
let input;
|
||||
let layer;
|
||||
const addedLayers = [];
|
||||
const removedLayers = [];
|
||||
this.handlingClick = true;
|
||||
for (let i = inputs.length - 1; i >= 0; i--) {
|
||||
input = inputs[i];
|
||||
layer = this.layerService.getLayer(input.layerId);
|
||||
|
||||
if (input.checked) {
|
||||
addedLayers.push(layer);
|
||||
} else if (!input.checked) {
|
||||
removedLayers.push(layer);
|
||||
}
|
||||
}
|
||||
removedLayers.forEach((l: any) => {
|
||||
l.hide();
|
||||
});
|
||||
addedLayers.forEach((l: any) => {
|
||||
l.show();
|
||||
});
|
||||
|
||||
this.handlingClick = false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
import { IMapService } from '@l7/core';
|
||||
import { bindAll, DOM } from '@l7/utils';
|
||||
import Control, { IControlOption, PositionType } from './BaseControl';
|
||||
export default class Logo extends Control {
|
||||
public getDefault() {
|
||||
return {
|
||||
position: PositionType.BOTTOMLEFT,
|
||||
};
|
||||
}
|
||||
public onAdd(MapService: IMapService) {
|
||||
const className = 'l7-control-logo';
|
||||
const container = DOM.create('div', className);
|
||||
const anchor: HTMLLinkElement = DOM.create(
|
||||
'a',
|
||||
'l7-ctrl-logo',
|
||||
) as HTMLLinkElement;
|
||||
anchor.target = '_blank';
|
||||
anchor.rel = 'noopener nofollow';
|
||||
anchor.href = 'https://antv.alipay.com/l7';
|
||||
anchor.setAttribute('aria-label', 'AntV logo');
|
||||
anchor.setAttribute('rel', 'noopener nofollow');
|
||||
container.appendChild(anchor);
|
||||
return container;
|
||||
}
|
||||
}
|
|
@ -279,7 +279,7 @@
|
|||
.l7-right .l7-control {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
|
||||
/* attribution and scale controls */
|
||||
|
||||
.l7-control-container .l7-control-attribution {
|
||||
|
@ -350,6 +350,406 @@
|
|||
background: #fff;
|
||||
border-radius: 5px;
|
||||
}
|
||||
.l7-control-layers-toggle {
|
||||
background-image: url(../images/layers.png);
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
}
|
||||
.l7-retina .l7-control-layers-toggle {
|
||||
background-image: url(../images/layers.png);
|
||||
background-size: 26px 26px;
|
||||
}
|
||||
.l7-touch .l7-control-layers-toggle {
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
}
|
||||
.l7-control-layers .l7-control-layers-list,
|
||||
.l7-control-layers-expanded .l7-control-layers-toggle {
|
||||
display: none;
|
||||
}
|
||||
.l7-control-layers-expanded .l7-control-layers-list {
|
||||
display: block;
|
||||
position: relative;
|
||||
font-size: 19px;
|
||||
padding: 8px;
|
||||
}
|
||||
.l7-control-layers-expanded {
|
||||
padding: 6px 10px 6px 6px;
|
||||
color: #333;
|
||||
background: #fff;
|
||||
}
|
||||
.l7-control-layers-scrollbar {
|
||||
overflow-y: scroll;
|
||||
overflow-x: hidden;
|
||||
padding-right: 5px;
|
||||
}
|
||||
.l7-cont.l7-marker {
|
||||
position: absolute !important;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 5;
|
||||
}
|
||||
.l7-popup-anchor-top,
|
||||
.l7-popup-anchor-top-left,
|
||||
.l7-popup-anchor-top-right {
|
||||
-webkit-flex-direction: column;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.l7-popup-anchor-bottom,
|
||||
.l7-popup-anchor-bottom-left,
|
||||
.l7-popup-anchor-bottom-right {
|
||||
-webkit-flex-direction: column-reverse;
|
||||
flex-direction: column-reverse;
|
||||
}
|
||||
|
||||
.l7-popup-anchor-left {
|
||||
-webkit-flex-direction: row;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.l7-popup-anchor-right {
|
||||
-webkit-flex-direction: row-reverse;
|
||||
flex-direction: row-reverse;
|
||||
}
|
||||
.l7-popup {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
display: -webkit-flex;
|
||||
display: flex;
|
||||
will-change: transform;
|
||||
pointer-events: none;
|
||||
z-index: 5;
|
||||
}
|
||||
.l7-popup-tip {
|
||||
width: 0;
|
||||
height: 0;
|
||||
border: 10px solid transparent;
|
||||
z-index: 1;
|
||||
}
|
||||
.l7-popup-anchor-top .l7-popup-tip {
|
||||
-webkit-align-self: center;
|
||||
align-self: center;
|
||||
border-top: none;
|
||||
border-bottom-color: #fff;
|
||||
}
|
||||
|
||||
.l7-popup-anchor-top-left .l7-popup-tip {
|
||||
-webkit-align-self: flex-start;
|
||||
align-self: flex-start;
|
||||
border-top: none;
|
||||
border-left: none;
|
||||
border-bottom-color: #fff;
|
||||
}
|
||||
|
||||
.l7-popup-anchor-top-right .l7-popup-tip {
|
||||
-webkit-align-self: flex-end;
|
||||
align-self: flex-end;
|
||||
border-top: none;
|
||||
border-right: none;
|
||||
border-bottom-color: #fff;
|
||||
}
|
||||
|
||||
.l7-popup-anchor-bottom .l7-popup-tip {
|
||||
-webkit-align-self: center;
|
||||
align-self: center;
|
||||
border-bottom: none;
|
||||
border-top-color: #fff;
|
||||
}
|
||||
|
||||
.l7-popup-anchor-bottom-left .l7-popup-tip {
|
||||
-webkit-align-self: flex-start;
|
||||
align-self: flex-start;
|
||||
border-bottom: none;
|
||||
border-left: none;
|
||||
border-top-color: #fff;
|
||||
}
|
||||
|
||||
.l7-popup-anchor-bottom-right .l7-popup-tip {
|
||||
-webkit-align-self: flex-end;
|
||||
align-self: flex-end;
|
||||
border-bottom: none;
|
||||
border-right: none;
|
||||
border-top-color: #fff;
|
||||
}
|
||||
|
||||
.l7-popup-anchor-left .l7-popup-tip {
|
||||
-webkit-align-self: center;
|
||||
align-self: center;
|
||||
border-left: none;
|
||||
border-right-color: #fff;
|
||||
}
|
||||
|
||||
.l7-popup-anchor-right .l7-popup-tip {
|
||||
-webkit-align-self: center;
|
||||
align-self: center;
|
||||
border-right: none;
|
||||
border-left-color: #fff;
|
||||
}
|
||||
|
||||
.l7-popup-close-button {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
border: 0;
|
||||
padding: 0;
|
||||
font-size: 25px;
|
||||
line-height: 20px;
|
||||
border-radius: 0 3px 0 0;
|
||||
cursor: pointer;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.l7-popup-close-button:hover {
|
||||
background-color: rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.l7-popup-content {
|
||||
position: relative;
|
||||
background: #fff;
|
||||
border-radius: 3px;
|
||||
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
|
||||
padding: 10px 10px 15px;
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
.l7-popup-anchor-top-left .l7-popup-content {
|
||||
border-top-left-radius: 0;
|
||||
}
|
||||
|
||||
.l7-popup-anchor-top-right .l7-popup-content {
|
||||
border-top-right-radius: 0;
|
||||
}
|
||||
|
||||
.l7-popup-anchor-bottom-left .l7-popup-content {
|
||||
border-bottom-left-radius: 0;
|
||||
}
|
||||
|
||||
.l7-popup-anchor-bottom-right .l7-popup-content {
|
||||
border-bottom-right-radius: 0;
|
||||
}
|
||||
|
||||
.l7-popup-track-pointer {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.l7-popup-track-pointer * {
|
||||
pointer-events: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.l7-map:hover .l7-popup-track-pointer {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.l7-map:active .l7-popup-track-pointer {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.l7-popup-close-button {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
border: 0;
|
||||
border-radius: 0 3px 0 0;
|
||||
cursor: pointer;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.l7-popup-close-button:hover {
|
||||
background-color: rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.l7-popup-content {
|
||||
position: relative;
|
||||
background: #fff;
|
||||
border-radius: 3px;
|
||||
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
|
||||
padding: 10px 10px 15px;
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
/* general toolbar styles */
|
||||
|
||||
.l7-bar {
|
||||
box-shadow: 0 1px 5px rgba(0,0,0,0.65);
|
||||
border-radius: 4px;
|
||||
}
|
||||
.l7-bar a,
|
||||
.l7-bar a:hover {
|
||||
background-color: #fff;
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
line-height: 30px;
|
||||
font-size: 30px;
|
||||
display: block;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
color: #8E9DAB;
|
||||
}
|
||||
.l7-bar a,
|
||||
.l7-control-layers-toggle {
|
||||
background-position: 50% 50%;
|
||||
background-repeat: no-repeat;
|
||||
display: block;
|
||||
}
|
||||
.l7-bar a:hover {
|
||||
background-color: #f4f4f4;
|
||||
}
|
||||
.l7-bar a:first-child {
|
||||
border-top-left-radius: 4px;
|
||||
border-top-right-radius: 4px;
|
||||
}
|
||||
.l7-bar a:last-child {
|
||||
border-bottom-left-radius: 4px;
|
||||
border-bottom-right-radius: 4px;
|
||||
border-bottom: none;
|
||||
}
|
||||
.l7-bar a.l7-disabled {
|
||||
cursor: default;
|
||||
background-color: #f4f4f4;
|
||||
color: #bbb;
|
||||
}
|
||||
|
||||
|
||||
/* control positioning */
|
||||
|
||||
.l7-control-container {
|
||||
font: 12px/1.5 "Helvetica Neue", Arial, Helvetica, sans-serif;
|
||||
}
|
||||
.l7-control-hide {
|
||||
display: none;
|
||||
}
|
||||
.l7-control {
|
||||
position: relative;
|
||||
z-index: 800;
|
||||
pointer-events: visiblePainted; /* IE 9-10 doesn't have auto */
|
||||
pointer-events: auto;
|
||||
}
|
||||
.l7-top,
|
||||
.l7-bottom {
|
||||
position: absolute;
|
||||
z-index: 1000;
|
||||
pointer-events: none;
|
||||
}
|
||||
.l7-top {
|
||||
top: 0;
|
||||
}
|
||||
.l7-right {
|
||||
right: 0;
|
||||
}
|
||||
.l7-bottom {
|
||||
bottom: 0;
|
||||
}
|
||||
.l7-left {
|
||||
left: 0;
|
||||
}
|
||||
.l7-control {
|
||||
float: left;
|
||||
clear: both;
|
||||
}
|
||||
.l7-right .l7-control {
|
||||
float: right;
|
||||
}
|
||||
.l7-top .l7-control {
|
||||
margin-top: 10px;
|
||||
}
|
||||
.l7-bottom .l7-control {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.l7-left .l7-control {
|
||||
margin-left: 10px;
|
||||
}
|
||||
.l7-right .l7-control {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
/* attribution and scale controls */
|
||||
|
||||
.l7-control-container .l7-control-attribution {
|
||||
background: #fff;
|
||||
background: rgba(255, 255, 255, 0.7);
|
||||
margin: 0;
|
||||
}
|
||||
.l7-control-attribution,
|
||||
.l7-control-scale-line {
|
||||
padding: 0 5px;
|
||||
color: #333;
|
||||
}
|
||||
.l7-control-attribution a {
|
||||
text-decoration: none;
|
||||
}
|
||||
.l7-control-attribution a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
.l7-container .l7-control-attribution,
|
||||
.l7-container .l7-control-scale {
|
||||
font-size: 11px;
|
||||
padding: 5px 5px 2px 5px;
|
||||
background: rgba(255, 255, 255, 0.7);
|
||||
}
|
||||
.l7-left .l7-control-scale {
|
||||
margin-left: 5px;
|
||||
}
|
||||
.l7-bottom .l7-control-scale {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
.l7-control-scale-line {
|
||||
border: 2px solid #8E9DAB;
|
||||
border-top: none;
|
||||
color: #8e9dab;
|
||||
line-height: 1.1;
|
||||
padding: 2px 5px 1px;
|
||||
font-size: 11px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
|
||||
background: #fff;
|
||||
background: rgba(255, 255, 255, 0.8);
|
||||
}
|
||||
.l7-control-scale-line:not(:first-child) {
|
||||
border-top: 2px solid #777;
|
||||
border-bottom: none;
|
||||
margin-top: -2px;
|
||||
}
|
||||
.l7-control-scale-line:not(:first-child):not(:last-child) {
|
||||
border-bottom: 2px solid #777;
|
||||
}
|
||||
|
||||
.l7-touch .l7-control-attribution,
|
||||
.l7-touch .l7-control-layers,
|
||||
.l7-touch .l7-bar {
|
||||
box-shadow: none;
|
||||
}
|
||||
.l7-touch .l7-control-layers,
|
||||
.l7-touch .l7-bar {
|
||||
border: 2px solid rgba(0,0,0,0.2);
|
||||
background-clip: padding-box;
|
||||
}
|
||||
/*logo */
|
||||
|
||||
.l7-ctrl-logo {
|
||||
background-size: 100% 100%;
|
||||
width: 89px;
|
||||
height: 16px;
|
||||
margin: 0 0 -3px -3px;
|
||||
display: block;
|
||||
background-repeat: no-repeat;
|
||||
cursor: pointer;
|
||||
background-image: url('../images/logo.png');
|
||||
}
|
||||
|
||||
|
||||
/* layers control */
|
||||
|
||||
.l7-control-layers {
|
||||
box-shadow: 0 1px 8px rgba(0,0,0,0.4);
|
||||
background: #fff;
|
||||
border-radius: 2px;
|
||||
}
|
||||
.l7-control-layers-toggle {
|
||||
background-image: url(../images/layers.svg);
|
||||
width: 36px;
|
||||
|
@ -373,7 +773,7 @@
|
|||
}
|
||||
.l7-control-layers-expanded {
|
||||
padding: 6px 10px 6px 6px;
|
||||
color: #333;
|
||||
color: #59626B;
|
||||
background: #fff;
|
||||
}
|
||||
.l7-control-layers-scrollbar {
|
||||
|
@ -387,11 +787,27 @@
|
|||
top: 1px;
|
||||
}
|
||||
.l7-control-layers label {
|
||||
display: block;
|
||||
}
|
||||
display: block;
|
||||
padding: 8px;
|
||||
}
|
||||
.l7-control-layers label input[type="radio"], input[type="checkbox"]{
|
||||
width: 19px;
|
||||
height: 19px;
|
||||
margin: 0;
|
||||
vertical-align: middle;
|
||||
-ms-transform: scale(1.5); /* IE 9 */
|
||||
-webkit-transform: scale(1.5); /* Chrome, Safari, Opera */
|
||||
transform: scale(1.5);
|
||||
}
|
||||
.l7-control-layers-separator {
|
||||
height: 0;
|
||||
border-top: 1px solid #ddd;
|
||||
border-top: 1px solid #D8D8D8;
|
||||
margin: 5px -10px 5px -6px;
|
||||
}
|
||||
|
||||
.l7-control-layers-selector {
|
||||
margin-top: 2px;
|
||||
position: relative;
|
||||
top: 1px;
|
||||
}
|
||||
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 1.9 KiB |
|
@ -0,0 +1 @@
|
|||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1566292427369" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="8341" width="32" height="32" xmlns:xlink="http://www.w3.org/1999/xlink"><defs><style type="text/css"></style></defs><path d="M256 341.333333l256 128 256-128-256-128-256 128z m276.864-208.384l341.034667 173.909334c20.736 10.581333 28.202667 34.56 16.682666 53.632a41.386667 41.386667 0 0 1-16.64 15.317333l-341.077333 173.909333a46.336 46.336 0 0 1-41.728 0L150.101333 375.808c-20.736-10.581333-28.202667-34.56-16.682666-53.632a41.386667 41.386667 0 0 1 16.64-15.317333l341.077333-173.909334c12.970667-6.613333 28.757333-6.613333 41.728 0z m0 587.349334a45.653333 45.653333 0 0 1-41.728 0l-341.034667-176.938667c-20.736-10.752-28.202667-35.157333-16.682666-54.528a41.642667 41.642667 0 0 1 16.64-15.573333 34.901333 34.901333 0 0 1 32.213333 0l308.906667 160.213333c12.928 6.741333 28.714667 6.741333 41.685333 0l308.864-160.213333a34.901333 34.901333 0 0 1 32.170667 0c20.736 10.752 28.202667 35.157333 16.682666 54.528a41.642667 41.642667 0 0 1-16.64 15.573333l-341.077333 176.938667z m0 170.666666a45.653333 45.653333 0 0 1-41.728 0l-341.034667-176.938666c-20.736-10.752-28.202667-35.157333-16.682666-54.528a41.642667 41.642667 0 0 1 16.64-15.573334 34.901333 34.901333 0 0 1 32.213333 0l308.906667 160.213334c12.928 6.741333 28.714667 6.741333 41.685333 0l308.864-160.213334a34.901333 34.901333 0 0 1 32.170667 0c20.736 10.752 28.202667 35.157333 16.682666 54.528a41.642667 41.642667 0 0 1-16.64 15.573334l-341.077333 176.938666z" fill="#000000" p-id="8342"></path></svg>
|
After Width: | Height: | Size: 1.7 KiB |
Binary file not shown.
After Width: | Height: | Size: 8.2 KiB |
|
@ -1,6 +1,9 @@
|
|||
import Control from './control/BaseControl';
|
||||
import Layers from './control/layer';
|
||||
import Logo from './control/logo';
|
||||
import Scale from './control/scale';
|
||||
import Zoom from './control/zoom';
|
||||
import Marker from './marker';
|
||||
import Popup from './popup';
|
||||
export { Control, Scale, Zoom, Marker, Popup };
|
||||
|
||||
export { Control, Logo, Scale, Zoom, Layers, Marker, Popup };
|
||||
|
|
|
@ -61,7 +61,7 @@ container
|
|||
container
|
||||
.bind<ILayerService>(TYPES.ILayerService)
|
||||
.to(LayerService)
|
||||
.inTransientScope();
|
||||
.inSingletonScope();
|
||||
container
|
||||
.bind<IStyleAttributeService>(TYPES.IStyleAttributeService)
|
||||
.to(StyleAttributeService);
|
||||
|
@ -100,7 +100,7 @@ container
|
|||
|
||||
// @see https://github.com/inversify/InversifyJS/blob/master/wiki/inheritance.md#what-can-i-do-when-my-base-class-is-provided-by-a-third-party-module
|
||||
decorate(injectable(), EventEmitter);
|
||||
|
||||
container.bind(TYPES.IEventEmitter).to(EventEmitter);
|
||||
// 支持 L7 使用 new 而非容器实例化的场景,同时禁止 lazyInject cache
|
||||
// @see https://github.com/inversify/inversify-inject-decorators#caching-vs-non-caching-behaviour
|
||||
const DECORATORS = getDecorators(container, false);
|
||||
|
|
|
@ -24,7 +24,6 @@ export default class ControlService implements IControlService {
|
|||
ctr.addTo(mapService); // scene对象
|
||||
this.controls.push(ctr);
|
||||
}
|
||||
|
||||
public removeControl(ctr: IControl): this {
|
||||
const index = this.controls.indexOf(ctr);
|
||||
if (index > -1) {
|
||||
|
|
|
@ -6,6 +6,7 @@ import { ISource, ISourceCFG } from '../source/ISourceService';
|
|||
import {
|
||||
IEncodeFeature,
|
||||
IScale,
|
||||
IScaleOptions,
|
||||
IStyleAttributeService,
|
||||
StyleAttrField,
|
||||
StyleAttributeOption,
|
||||
|
@ -32,8 +33,10 @@ export interface IPickedFeature {
|
|||
export interface ILayer {
|
||||
id: string; // 一个场景中同一类型 Layer 可能存在多个
|
||||
name: string; // 代表 Layer 的类型
|
||||
// visible: boolean;
|
||||
// zIndex: number;
|
||||
visible: boolean;
|
||||
zIndex: number;
|
||||
minZoom: number;
|
||||
maxZoom: number;
|
||||
configService: IGlobalConfigService;
|
||||
plugins: ILayerPlugin[];
|
||||
hooks: {
|
||||
|
@ -62,8 +65,12 @@ export interface ILayer {
|
|||
// filter(field: string, value: StyleAttributeOption): ILayer;
|
||||
// active(option: ActiveOption): ILayer;
|
||||
style(options: unknown): ILayer;
|
||||
// hide(): ILayer;
|
||||
// show(): ILayer;
|
||||
hide(): ILayer;
|
||||
show(): ILayer;
|
||||
setIndex(index: number): ILayer;
|
||||
isVisible(): boolean;
|
||||
setMaxZoom(min: number): ILayer;
|
||||
setMinZoom(max: number): ILayer;
|
||||
// animate(field: string, option: any): ILayer;
|
||||
render(): ILayer;
|
||||
destroy(): void;
|
||||
|
@ -74,6 +81,13 @@ export interface ILayer {
|
|||
setEncodedData(encodedData: IEncodeFeature[]): void;
|
||||
getEncodedData(): IEncodeFeature[];
|
||||
getStyleOptions(): Partial<ILayerInitializationOptions>;
|
||||
getScaleOptions(): IScaleOptions;
|
||||
/**
|
||||
* 事件
|
||||
*/
|
||||
on(type: string, hander: (...args: any[]) => void): void;
|
||||
off(type: string, hander: (...args: any[]) => void): void;
|
||||
once(type: string, hander: (...args: any[]) => void): void;
|
||||
/**
|
||||
* JSON Schema 用于校验配置项
|
||||
*/
|
||||
|
@ -96,6 +110,10 @@ export interface ILayerPlugin {
|
|||
* Layer 初始化参数
|
||||
*/
|
||||
export interface ILayerInitializationOptions {
|
||||
minZoom: number;
|
||||
maxZoom: number;
|
||||
visible: boolean;
|
||||
zIndex: number;
|
||||
enableMultiPassRenderer: boolean;
|
||||
passes: Array<string | [string, { [key: string]: unknown }]>;
|
||||
|
||||
|
@ -129,6 +147,10 @@ export interface ILayerInitializationOptions {
|
|||
export interface ILayerService {
|
||||
add(layer: ILayer): void;
|
||||
initLayers(): void;
|
||||
getLayers(): ILayer[];
|
||||
getLayer(name: string): ILayer | undefined;
|
||||
remove(layer: ILayer): void;
|
||||
updateRenderOrder(): void;
|
||||
renderLayers(): void;
|
||||
destroy(): void;
|
||||
}
|
||||
|
|
|
@ -46,6 +46,9 @@ export interface IScaleOption {
|
|||
format?: () => any;
|
||||
domain?: any[];
|
||||
}
|
||||
export interface IScaleOptions {
|
||||
[key: string]: IScale;
|
||||
}
|
||||
export interface IStyleScale {
|
||||
scale: any;
|
||||
field: string;
|
||||
|
|
|
@ -16,9 +16,11 @@ export default class LayerService implements ILayerService {
|
|||
private readonly configService: IGlobalConfigService;
|
||||
|
||||
public add(layer: ILayer) {
|
||||
this.layers.push(layer);
|
||||
this.initPlugin(layer);
|
||||
layer.init();
|
||||
this.layers.push(layer);
|
||||
// 添加完成需要触发重绘
|
||||
// this.renderLayers();
|
||||
}
|
||||
|
||||
public initLayers() {
|
||||
|
@ -31,10 +33,29 @@ export default class LayerService implements ILayerService {
|
|||
});
|
||||
}
|
||||
|
||||
public getLayers(): ILayer[] {
|
||||
return this.layers;
|
||||
}
|
||||
|
||||
public getLayer(id: string): ILayer | undefined {
|
||||
return this.layers.find((layer) => layer.id === id);
|
||||
}
|
||||
|
||||
public remove(layer: ILayer): void {
|
||||
const layerIndex = this.layers.indexOf(layer);
|
||||
if (layerIndex > -1) {
|
||||
this.layers.splice(layerIndex, 1);
|
||||
}
|
||||
layer.destroy();
|
||||
this.renderLayers();
|
||||
}
|
||||
|
||||
public renderLayers() {
|
||||
// TODO:脏检查,只渲染发生改变的 Layer
|
||||
//
|
||||
this.clear();
|
||||
this.layers
|
||||
// .filter((layer) => layer.isDirty())
|
||||
.filter((layer) => layer.isVisible())
|
||||
.forEach((layer) => {
|
||||
// trigger hooks
|
||||
layer.hooks.beforeRender.call();
|
||||
|
@ -43,6 +64,13 @@ export default class LayerService implements ILayerService {
|
|||
});
|
||||
}
|
||||
|
||||
public updateRenderOrder() {
|
||||
this.layers.sort((pre: ILayer, next: ILayer) => {
|
||||
return pre.zIndex - next.zIndex;
|
||||
});
|
||||
this.renderLayers();
|
||||
}
|
||||
|
||||
public destroy() {
|
||||
this.layers.forEach((layer) => layer.destroy());
|
||||
this.layers = [];
|
||||
|
@ -52,4 +80,11 @@ export default class LayerService implements ILayerService {
|
|||
plugin.apply(layer);
|
||||
}
|
||||
}
|
||||
private clear() {
|
||||
this.renderService.clear({
|
||||
color: [0, 0, 0, 0],
|
||||
depth: 1,
|
||||
framebuffer: null,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@ export interface IMapService {
|
|||
|
||||
on(type: string, hander: (...args: any[]) => void): void;
|
||||
off(type: string, hander: (...args: any[]) => void): void;
|
||||
once(type: string, hander: (...args: any[]) => void): void;
|
||||
// get dom
|
||||
getContainer(): HTMLElement | null;
|
||||
getSize(): [number, number];
|
||||
|
@ -59,6 +60,8 @@ export enum MapType {
|
|||
mapbox = 'mapbox',
|
||||
}
|
||||
|
||||
export const MapServiceEvent = ['mapload'];
|
||||
|
||||
/**
|
||||
* 地图初始化配置项
|
||||
*/
|
||||
|
|
|
@ -87,7 +87,6 @@ export default class BasePostProcessingPass<InitializationOptions = {}>
|
|||
const postProcessor = layer.multiPassRenderer.getPostProcessor();
|
||||
const { useFramebuffer, getViewportSize } = this.rendererService;
|
||||
const { width, height } = getViewportSize();
|
||||
|
||||
useFramebuffer(
|
||||
this.renderToScreen ? null : postProcessor.getWriteFBO(),
|
||||
() => {
|
||||
|
|
|
@ -52,7 +52,6 @@ export default class PostProcessor implements IPostProcessor {
|
|||
public async render(layer: ILayer) {
|
||||
for (let i = 0; i < this.passes.length; i++) {
|
||||
const pass = this.passes[i];
|
||||
|
||||
// last pass should render to screen
|
||||
pass.setRenderToScreen(this.isLastEnabledPass(i));
|
||||
await pass.render(layer);
|
||||
|
|
|
@ -13,4 +13,5 @@ export interface ISceneService {
|
|||
render(): void;
|
||||
destroy(): void;
|
||||
}
|
||||
export const SceneEventList = ['loaded', 'resize', 'destroy'];
|
||||
// scene 事件
|
||||
export const SceneEventList = ['loaded', 'maploaded', 'resize', 'destroy'];
|
||||
|
|
|
@ -161,7 +161,7 @@ export default class Scene extends EventEmitter implements ISceneService {
|
|||
this.map.addMarkerContainer();
|
||||
this.inited = true;
|
||||
|
||||
// this.layerService.initLayers();
|
||||
this.layerService.renderLayers();
|
||||
this.emit('loaded');
|
||||
}
|
||||
|
||||
|
|
|
@ -63,6 +63,14 @@ vec3 project_offset_normal(vec3 vector) {
|
|||
return project_normal(vector);
|
||||
}
|
||||
|
||||
// reverse Y
|
||||
vec3 reverse_offset_normal(vec3 vector) {
|
||||
if (u_CoordinateSystem == COORDINATE_SYSTEM_P20) {
|
||||
return vector * vec3(1.0,-1.0, 1.0);
|
||||
}
|
||||
return vector;
|
||||
}
|
||||
|
||||
vec4 project_position(vec4 position) {
|
||||
if (u_CoordinateSystem == COORDINATE_SYSTEM_LNGLAT_OFFSET
|
||||
|| u_CoordinateSystem == COORDINATE_SYSTEM_P20_OFFSET) {
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
const TYPES = {
|
||||
IEventEmitter: Symbol.for('IEventEmitter'),
|
||||
ISceneService: Symbol.for('ISceneService'),
|
||||
IGlobalConfigService: Symbol.for('IGlobalConfigService'),
|
||||
ICameraService: Symbol.for('ICameraService'),
|
||||
|
|
|
@ -9,11 +9,14 @@ import {
|
|||
ILayer,
|
||||
ILayerInitializationOptions,
|
||||
ILayerPlugin,
|
||||
ILayerService,
|
||||
IMapService,
|
||||
IModel,
|
||||
IModelInitializationOptions,
|
||||
IMultiPassRenderer,
|
||||
IRendererService,
|
||||
IScale,
|
||||
IScaleOptions,
|
||||
IShaderModuleService,
|
||||
ISourceCFG,
|
||||
IStyleAttributeService,
|
||||
|
@ -26,7 +29,8 @@ import {
|
|||
TYPES,
|
||||
} from '@l7/core';
|
||||
import Source from '@l7/source';
|
||||
import { isFunction } from 'lodash';
|
||||
import { EventEmitter } from 'eventemitter3';
|
||||
import { isFunction, isObject } from 'lodash';
|
||||
// @ts-ignore
|
||||
import mergeJsonSchemas from 'merge-json-schemas';
|
||||
import { SyncBailHook, SyncHook } from 'tapable';
|
||||
|
@ -51,7 +55,11 @@ let layerIdCounter = 0;
|
|||
const defaultLayerInitializationOptions: Partial<
|
||||
ILayerInitializationOptions
|
||||
> = {
|
||||
enableMultiPassRenderer: true,
|
||||
minZoom: 0,
|
||||
maxZoom: 20,
|
||||
visible: true,
|
||||
zIndex: 0,
|
||||
enableMultiPassRenderer: false,
|
||||
enablePicking: false,
|
||||
enableHighlight: false,
|
||||
highlightColor: 'red',
|
||||
|
@ -59,9 +67,14 @@ const defaultLayerInitializationOptions: Partial<
|
|||
jitterScale: 1,
|
||||
};
|
||||
|
||||
export default class BaseLayer<ChildLayerStyleOptions = {}> implements ILayer {
|
||||
export default class BaseLayer<ChildLayerStyleOptions = {}> extends EventEmitter
|
||||
implements ILayer {
|
||||
public id: string = `${layerIdCounter++}`;
|
||||
public name: string;
|
||||
public visible: boolean = true;
|
||||
public zIndex: number = 0;
|
||||
public minZoom: number;
|
||||
public maxZoom: number;
|
||||
|
||||
// 生命周期钩子
|
||||
public hooks = {
|
||||
|
@ -114,6 +127,9 @@ export default class BaseLayer<ChildLayerStyleOptions = {}> implements ILayer {
|
|||
@lazyInject(TYPES.IMapService)
|
||||
protected readonly map: IMapService;
|
||||
|
||||
@lazyInject(TYPES.ILayerService)
|
||||
protected readonly layerService: ILayerService;
|
||||
|
||||
private encodedData: IEncodeFeature[];
|
||||
|
||||
private configSchema: object;
|
||||
|
@ -124,6 +140,13 @@ export default class BaseLayer<ChildLayerStyleOptions = {}> implements ILayer {
|
|||
private styleOptions: Partial<
|
||||
ILayerInitializationOptions & ChildLayerStyleOptions
|
||||
>;
|
||||
private scaleOptions: IScaleOptions = {};
|
||||
|
||||
private enodeOptions: {
|
||||
[type: string]: {
|
||||
field: string;
|
||||
};
|
||||
};
|
||||
|
||||
@lazyInject(TYPES.IInteractionService)
|
||||
private readonly interactionService: IInteractionService;
|
||||
|
@ -131,10 +154,17 @@ export default class BaseLayer<ChildLayerStyleOptions = {}> implements ILayer {
|
|||
constructor(
|
||||
styleOptions: Partial<ILayerInitializationOptions & ChildLayerStyleOptions>,
|
||||
) {
|
||||
super();
|
||||
this.styleOptions = {
|
||||
...defaultLayerInitializationOptions,
|
||||
...styleOptions,
|
||||
};
|
||||
const { minZoom, maxZoom, zIndex, visible } = this
|
||||
.styleOptions as ILayerInitializationOptions;
|
||||
this.visible = visible;
|
||||
this.zIndex = zIndex;
|
||||
this.minZoom = minZoom;
|
||||
this.maxZoom = maxZoom;
|
||||
}
|
||||
|
||||
public addPlugin(plugin: ILayerPlugin) {
|
||||
|
@ -255,6 +285,17 @@ export default class BaseLayer<ChildLayerStyleOptions = {}> implements ILayer {
|
|||
};
|
||||
return this;
|
||||
}
|
||||
public scale(field: string | IScaleOptions, cfg: IScale) {
|
||||
if (isObject(field)) {
|
||||
this.scaleOptions = {
|
||||
...this.scaleOptions,
|
||||
...field,
|
||||
};
|
||||
} else {
|
||||
this.scaleOptions[field] = cfg;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
public render(): ILayer {
|
||||
if (this.multiPassRenderer && this.multiPassRenderer.getRenderFlag()) {
|
||||
this.multiPassRenderer.render();
|
||||
|
@ -263,6 +304,39 @@ export default class BaseLayer<ChildLayerStyleOptions = {}> implements ILayer {
|
|||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public show(): ILayer {
|
||||
this.visible = true;
|
||||
this.layerService.renderLayers();
|
||||
return this;
|
||||
}
|
||||
|
||||
public hide(): ILayer {
|
||||
this.visible = false;
|
||||
this.layerService.renderLayers();
|
||||
return this;
|
||||
}
|
||||
|
||||
public setIndex(index: number): ILayer {
|
||||
this.zIndex = index;
|
||||
this.layerService.updateRenderOrder();
|
||||
return this;
|
||||
}
|
||||
|
||||
public isVisible(): boolean {
|
||||
const zoom = this.map.getZoom();
|
||||
return this.visible && zoom >= this.minZoom && zoom <= this.maxZoom;
|
||||
}
|
||||
|
||||
public setMinZoom(min: number): ILayer {
|
||||
this.minZoom = min;
|
||||
return this;
|
||||
}
|
||||
|
||||
public setMaxZoom(max: number): ILayer {
|
||||
this.maxZoom = max;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* zoom to layer Bounds
|
||||
*/
|
||||
|
@ -304,6 +378,9 @@ export default class BaseLayer<ChildLayerStyleOptions = {}> implements ILayer {
|
|||
public getStyleOptions() {
|
||||
return this.styleOptions;
|
||||
}
|
||||
public getScaleOptions() {
|
||||
return this.scaleOptions;
|
||||
}
|
||||
|
||||
public setEncodedData(encodedData: IEncodeFeature[]) {
|
||||
this.encodedData = encodedData;
|
||||
|
|
|
@ -61,10 +61,10 @@ export function LineTriangulation(feature: IEncodeFeature) {
|
|||
const { coordinates } = feature;
|
||||
const line = getNormals(coordinates as number[][], false, 0);
|
||||
return {
|
||||
vertices: line.attrPos, // [ x,y,z, distance, miter ]
|
||||
vertices: line.attrPos, // [ x,y,z, distance, miter,total ]
|
||||
indices: line.attrIndex,
|
||||
normals: line.normals,
|
||||
size: 5,
|
||||
size: 6,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -87,7 +87,7 @@ export function HeatmapGridTriangulation(feature: IEncodeFeature) {
|
|||
| ShapeType2D
|
||||
| ShapeType3D);
|
||||
return {
|
||||
vertices: positions, // [ x, y, z ]
|
||||
vertices: positions, // [ x, y, z ] 多边形顶点
|
||||
indices: index,
|
||||
normals: Array.from(computeVertexNormals(positions, index)),
|
||||
size: 3,
|
||||
|
|
|
@ -43,19 +43,11 @@ export default class HeatMapGrid extends BaseLayer<IHeatMapLayerStyleOptions> {
|
|||
this.registerBuiltinAttributes(this);
|
||||
this.models = [
|
||||
this.buildLayerModel({
|
||||
moduleName: 'pointExtrude',
|
||||
moduleName: 'gridheatmap',
|
||||
vertexShader: heatmapGridVert,
|
||||
fragmentShader: heatmapGridFrag,
|
||||
triangulation: HeatmapGridTriangulation,
|
||||
blend: {
|
||||
enable: true,
|
||||
func: {
|
||||
srcRGB: gl.SRC_ALPHA,
|
||||
srcAlpha: 1,
|
||||
dstRGB: gl.ONE_MINUS_SRC_ALPHA,
|
||||
dstAlpha: 1,
|
||||
},
|
||||
},
|
||||
depth: { enable: false },
|
||||
}),
|
||||
];
|
||||
}
|
||||
|
|
|
@ -223,6 +223,15 @@ export default class HeatMapLayer extends BaseLayer<IHeatMapLayerStyleOptions> {
|
|||
depth: {
|
||||
enable: false,
|
||||
},
|
||||
blend: {
|
||||
enable: true,
|
||||
func: {
|
||||
srcRGB: gl.SRC_ALPHA,
|
||||
srcAlpha: 1,
|
||||
dstRGB: gl.ONE_MINUS_SRC_ALPHA,
|
||||
dstAlpha: 1,
|
||||
},
|
||||
},
|
||||
count: 6,
|
||||
elements: createElements({
|
||||
data: [0, 2, 1, 2, 3, 1],
|
||||
|
@ -254,7 +263,6 @@ export default class HeatMapLayer extends BaseLayer<IHeatMapLayerStyleOptions> {
|
|||
}
|
||||
private draw3DHeatMap() {
|
||||
const { opacity } = this.getStyleOptions();
|
||||
const mapbounds = this.map.getBounds();
|
||||
const invert = mat4.invert(
|
||||
mat4.create(),
|
||||
// @ts-ignore
|
||||
|
@ -265,7 +273,6 @@ export default class HeatMapLayer extends BaseLayer<IHeatMapLayerStyleOptions> {
|
|||
u_opacity: opacity || 1.0,
|
||||
u_colorTexture: this.colorTexture,
|
||||
u_texture: this.heatmapFramerBuffer,
|
||||
u_extent: [-179.9476, -60.0959, 179.9778, 79.5651],
|
||||
u_InverseViewProjectionMatrix: [...invert],
|
||||
},
|
||||
});
|
||||
|
@ -273,7 +280,7 @@ export default class HeatMapLayer extends BaseLayer<IHeatMapLayerStyleOptions> {
|
|||
private build3dHeatMap() {
|
||||
const { getViewportSize } = this.rendererService;
|
||||
const { width, height } = getViewportSize();
|
||||
const triangulation = heatMap3DTriangulation(width / 2.0, height / 2.0);
|
||||
const triangulation = heatMap3DTriangulation(width / 4.0, height / 4.0);
|
||||
this.shaderModuleService.registerModule('heatmap3dColor', {
|
||||
vs: heatmap3DVert,
|
||||
fs: heatmap3DFrag,
|
||||
|
@ -312,7 +319,7 @@ export default class HeatMapLayer extends BaseLayer<IHeatMapLayerStyleOptions> {
|
|||
...uniforms,
|
||||
},
|
||||
depth: {
|
||||
enable: false,
|
||||
enable: true,
|
||||
},
|
||||
blend: {
|
||||
enable: true,
|
||||
|
|
|
@ -2,16 +2,17 @@ uniform sampler2D u_texture;
|
|||
uniform sampler2D u_colorTexture;
|
||||
uniform float u_opacity;
|
||||
varying vec2 v_texCoord;
|
||||
varying float v_intensity;
|
||||
|
||||
void main(){
|
||||
float intensity = texture2D(u_texture, v_texCoord).r;
|
||||
vec2 ramp_pos = vec2(
|
||||
fract(16.0 * (1.0 - intensity)),
|
||||
floor(16.0 * (1.0 - intensity)) / 16.0);
|
||||
fract(16.0 * (1.0 - v_intensity)),
|
||||
floor(16.0 * (1.0 - v_intensity)) / 16.0);
|
||||
// vec4 color = texture2D(u_colorTexture,vec2(0.5,1.0-intensity));
|
||||
vec4 color = texture2D(u_colorTexture,ramp_pos);
|
||||
gl_FragColor = color;
|
||||
gl_FragColor.a = color.a * smoothstep(0.0, 0.05,intensity) * u_opacity;
|
||||
// gl_FragColor.a = color.a * smoothstep(0.0, 0.01, v_intensity) * u_opacity;
|
||||
// gl_FragColor.a = 0.2;
|
||||
|
||||
}
|
||||
|
|
|
@ -2,10 +2,10 @@ precision highp float;
|
|||
attribute vec3 a_Position;
|
||||
attribute vec2 a_Uv;
|
||||
uniform sampler2D u_texture;
|
||||
uniform vec4 u_extent;
|
||||
varying vec2 v_texCoord;
|
||||
uniform mat4 u_ModelMatrix;
|
||||
uniform mat4 u_InverseViewProjectionMatrix;
|
||||
varying float v_intensity;
|
||||
|
||||
vec2 toBezier(float t, vec2 P0, vec2 P1, vec2 P2, vec2 P3) {
|
||||
float t2 = t * t;
|
||||
|
@ -20,7 +20,8 @@ vec2 toBezier(float t, vec4 p){
|
|||
void main() {
|
||||
v_texCoord = a_Uv;
|
||||
|
||||
vec2 pos = a_Uv * vec2(2.0) - vec2(1.0);
|
||||
vec2 pos = 1.8 * (a_Uv * vec2(2.0) - vec2(1.0));
|
||||
|
||||
|
||||
vec4 p1 = vec4(pos, 0.0, 1.0);
|
||||
vec4 p2 = vec4(pos, 1.0, 1.0);
|
||||
|
@ -37,8 +38,8 @@ void main() {
|
|||
vec4 b= vec4(0.5000, 0, 1, 0.5000);
|
||||
float fh;
|
||||
|
||||
float intensity = texture2D(u_texture, v_texCoord).r;
|
||||
fh = toBezier(intensity, b).y;
|
||||
gl_Position = project_common_position_to_clipspace(vec4(position.xy, fh * 100., 1.0));
|
||||
v_intensity = texture2D(u_texture, v_texCoord).r;
|
||||
fh = toBezier(v_intensity, b).y;
|
||||
gl_Position = project_common_position_to_clipspace(vec4(position.xy, v_intensity * 50., 1.0));
|
||||
|
||||
}
|
||||
|
|
|
@ -11,6 +11,6 @@ void main(){
|
|||
// vec4 color = texture2D(u_colorTexture,vec2(0.5,1.0-intensity));
|
||||
vec4 color = texture2D(u_colorTexture,ramp_pos);
|
||||
gl_FragColor = color;
|
||||
gl_FragColor.a = color.a * smoothstep(0.1,0.5,intensity) * u_opacity;
|
||||
gl_FragColor.a = color.a * smoothstep(0.,0.01,intensity) * u_opacity;
|
||||
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ varying vec4 v_color;
|
|||
void main() {
|
||||
v_color = a_Color;
|
||||
mat2 rotationMatrix = mat2(cos(u_angle), sin(u_angle), -sin(u_angle), cos(u_angle));
|
||||
vec2 offset =(vec2(a_Position.xy * u_radius * u_coverage * rotationMatrix));
|
||||
vec2 offset =(vec2(a_Position.xy * u_radius * rotationMatrix ));
|
||||
vec4 project_pos = project_position(vec4(a_Pos.xy + offset, 0, 1.0));
|
||||
gl_Position = project_common_position_to_clipspace(vec4(project_pos.xy, 0., 1.0));
|
||||
}
|
||||
|
|
|
@ -4,6 +4,8 @@ import HeatMapGridLayer from './heatmap/grid';
|
|||
import HeatMapLayer from './heatmap/heatmap';
|
||||
import ArcLineLayer from './line/arc';
|
||||
import Arc2DLineLayer from './line/arc2d';
|
||||
import Arc3DLineLayer from './line/arc3d';
|
||||
import DashLineLayer from './line/dash';
|
||||
import LineLayer from './line/index';
|
||||
import Point3dLayer from './point/extrude';
|
||||
import PointLayer from './point/fill';
|
||||
|
@ -79,11 +81,13 @@ export {
|
|||
Point3dLayer,
|
||||
PointImageLayer,
|
||||
LineLayer,
|
||||
DashLineLayer,
|
||||
Polygon3DLayer,
|
||||
ImageLayer,
|
||||
HeatMapGridLayer,
|
||||
ArcLineLayer,
|
||||
Arc2DLineLayer,
|
||||
Arc3DLineLayer,
|
||||
RasterLayer,
|
||||
HeatMapLayer,
|
||||
TextLayer,
|
||||
|
|
|
@ -2,10 +2,11 @@ import { AttributeType, gl, IEncodeFeature, ILayer } from '@l7/core';
|
|||
import BaseLayer from '../core/BaseLayer';
|
||||
import { LineArcTriangulation } from '../core/triangulation';
|
||||
import line_arc_frag from './shaders/line_arc_frag.glsl';
|
||||
import line_arc_vert from './shaders/line_arc_vert.glsl';
|
||||
import line_arc2d_vert from './shaders/line_bezier_vert.glsl';
|
||||
interface IArcLayerStyleOptions {
|
||||
opacity: number;
|
||||
segmentNumber: number;
|
||||
blur: number;
|
||||
}
|
||||
export default class ArcLineLayer extends BaseLayer<IArcLayerStyleOptions> {
|
||||
public name: string = 'LineLayer';
|
||||
|
@ -23,12 +24,13 @@ export default class ArcLineLayer extends BaseLayer<IArcLayerStyleOptions> {
|
|||
}
|
||||
|
||||
protected renderModels() {
|
||||
const { opacity } = this.getStyleOptions();
|
||||
const { opacity, blur = 0.99 } = this.getStyleOptions();
|
||||
this.models.forEach((model) =>
|
||||
model.draw({
|
||||
uniforms: {
|
||||
u_opacity: opacity || 1,
|
||||
segmentNumber: 30,
|
||||
u_blur: blur,
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
@ -39,10 +41,11 @@ export default class ArcLineLayer extends BaseLayer<IArcLayerStyleOptions> {
|
|||
this.registerBuiltinAttributes(this);
|
||||
this.models = [
|
||||
this.buildLayerModel({
|
||||
moduleName: 'arcline',
|
||||
vertexShader: line_arc_vert,
|
||||
moduleName: 'arc2dline',
|
||||
vertexShader: line_arc2d_vert,
|
||||
fragmentShader: line_arc_frag,
|
||||
triangulation: LineArcTriangulation,
|
||||
depth: { enable: false },
|
||||
blend: {
|
||||
enable: true,
|
||||
func: {
|
||||
|
|
|
@ -6,8 +6,11 @@ import line_arc_frag from './shaders/line_arc_frag.glsl';
|
|||
interface IArcLayerStyleOptions {
|
||||
opacity: number;
|
||||
segmentNumber: number;
|
||||
blur: number;
|
||||
}
|
||||
export default class Arc2DLineLayer extends BaseLayer<IArcLayerStyleOptions> {
|
||||
export default class ArcCircleLineLayer extends BaseLayer<
|
||||
IArcLayerStyleOptions
|
||||
> {
|
||||
public name: string = 'LineLayer';
|
||||
|
||||
protected getConfigSchema() {
|
||||
|
@ -23,12 +26,13 @@ export default class Arc2DLineLayer extends BaseLayer<IArcLayerStyleOptions> {
|
|||
}
|
||||
|
||||
protected renderModels() {
|
||||
const { opacity } = this.getStyleOptions();
|
||||
const { opacity, blur = 0.99 } = this.getStyleOptions();
|
||||
this.models.forEach((model) =>
|
||||
model.draw({
|
||||
uniforms: {
|
||||
u_opacity: opacity || 1,
|
||||
segmentNumber: 30,
|
||||
u_blur: blur,
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
|
|
@ -0,0 +1,107 @@
|
|||
import { AttributeType, gl, IEncodeFeature, ILayer } from '@l7/core';
|
||||
import BaseLayer from '../core/BaseLayer';
|
||||
import { LineArcTriangulation } from '../core/triangulation';
|
||||
import line_arc_frag from './shaders/line_arc_frag.glsl';
|
||||
import line_arc_vert from './shaders/line_arc_vert.glsl';
|
||||
interface IArcLayerStyleOptions {
|
||||
opacity: number;
|
||||
segmentNumber: number;
|
||||
}
|
||||
export default class Arc3DLineLayer extends BaseLayer<IArcLayerStyleOptions> {
|
||||
public name: string = 'LineLayer';
|
||||
|
||||
protected getConfigSchema() {
|
||||
return {
|
||||
properties: {
|
||||
opacity: {
|
||||
type: 'number',
|
||||
minimum: 0,
|
||||
maximum: 1,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
protected renderModels() {
|
||||
const { opacity } = this.getStyleOptions();
|
||||
this.models.forEach((model) =>
|
||||
model.draw({
|
||||
uniforms: {
|
||||
u_opacity: opacity || 1,
|
||||
segmentNumber: 30,
|
||||
},
|
||||
}),
|
||||
);
|
||||
return this;
|
||||
}
|
||||
|
||||
protected buildModels() {
|
||||
this.registerBuiltinAttributes(this);
|
||||
this.models = [
|
||||
this.buildLayerModel({
|
||||
moduleName: 'arcline',
|
||||
vertexShader: line_arc_vert,
|
||||
fragmentShader: line_arc_frag,
|
||||
triangulation: LineArcTriangulation,
|
||||
blend: {
|
||||
enable: true,
|
||||
func: {
|
||||
srcRGB: gl.ONE,
|
||||
srcAlpha: 1,
|
||||
dstRGB: gl.ONE,
|
||||
dstAlpha: 1,
|
||||
},
|
||||
},
|
||||
}),
|
||||
];
|
||||
}
|
||||
|
||||
private registerBuiltinAttributes(layer: ILayer) {
|
||||
// point layer size;
|
||||
layer.styleAttributeService.registerStyleAttribute({
|
||||
name: 'size',
|
||||
type: AttributeType.Attribute,
|
||||
descriptor: {
|
||||
name: 'a_Size',
|
||||
buffer: {
|
||||
// give the WebGL driver a hint that this buffer may change
|
||||
usage: gl.DYNAMIC_DRAW,
|
||||
data: [],
|
||||
type: gl.FLOAT,
|
||||
},
|
||||
size: 1,
|
||||
update: (
|
||||
feature: IEncodeFeature,
|
||||
featureIdx: number,
|
||||
vertex: number[],
|
||||
attributeIdx: number,
|
||||
) => {
|
||||
const { size } = feature;
|
||||
return Array.isArray(size) ? [size[0]] : [size as number];
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
layer.styleAttributeService.registerStyleAttribute({
|
||||
name: 'instance', // 弧线起始点信息
|
||||
type: AttributeType.Attribute,
|
||||
descriptor: {
|
||||
name: 'a_Instance',
|
||||
buffer: {
|
||||
usage: gl.STATIC_DRAW,
|
||||
data: [],
|
||||
type: gl.FLOAT,
|
||||
},
|
||||
size: 4,
|
||||
update: (
|
||||
feature: IEncodeFeature,
|
||||
featureIdx: number,
|
||||
vertex: number[],
|
||||
attributeIdx: number,
|
||||
) => {
|
||||
return [vertex[3], vertex[4], vertex[5], vertex[6]];
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,206 @@
|
|||
import { AttributeType, gl, IEncodeFeature, ILayer } from '@l7/core';
|
||||
import BaseLayer from '../core/BaseLayer';
|
||||
import { LineTriangulation } from '../core/triangulation';
|
||||
import line_dash_frag from './shaders/line_dash_frag.glsl';
|
||||
import line_dash_vert from './shaders/line_dash_vert.glsl';
|
||||
interface IDashLineLayerStyleOptions {
|
||||
opacity: number;
|
||||
dashArray: [number, number];
|
||||
}
|
||||
export default class DashLineLayer extends BaseLayer<
|
||||
IDashLineLayerStyleOptions
|
||||
> {
|
||||
public name: string = 'LineLayer';
|
||||
|
||||
protected getConfigSchema() {
|
||||
return {
|
||||
properties: {
|
||||
opacity: {
|
||||
type: 'number',
|
||||
minimum: 0,
|
||||
maximum: 1,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
protected renderModels() {
|
||||
const { opacity, dashArray = [10, 5] } = this.getStyleOptions();
|
||||
this.models.forEach((model) =>
|
||||
model.draw({
|
||||
uniforms: {
|
||||
u_opacity: opacity || 1.0,
|
||||
u_dash_array: dashArray,
|
||||
},
|
||||
}),
|
||||
);
|
||||
return this;
|
||||
}
|
||||
|
||||
protected buildModels() {
|
||||
this.registerBuiltinAttributes(this);
|
||||
this.models = [
|
||||
this.buildLayerModel({
|
||||
moduleName: 'line_dash',
|
||||
vertexShader: line_dash_vert,
|
||||
fragmentShader: line_dash_frag,
|
||||
triangulation: LineTriangulation,
|
||||
blend: {
|
||||
enable: true,
|
||||
func: {
|
||||
srcRGB: gl.SRC_ALPHA,
|
||||
srcAlpha: 1,
|
||||
dstRGB: gl.ONE_MINUS_SRC_ALPHA,
|
||||
dstAlpha: 1,
|
||||
},
|
||||
},
|
||||
}),
|
||||
];
|
||||
}
|
||||
|
||||
private registerBuiltinAttributes(layer: ILayer) {
|
||||
// point layer size;
|
||||
layer.styleAttributeService.registerStyleAttribute({
|
||||
name: 'size',
|
||||
type: AttributeType.Attribute,
|
||||
descriptor: {
|
||||
name: 'a_Size',
|
||||
buffer: {
|
||||
// give the WebGL driver a hint that this buffer may change
|
||||
usage: gl.DYNAMIC_DRAW,
|
||||
data: [],
|
||||
type: gl.FLOAT,
|
||||
},
|
||||
size: 1,
|
||||
update: (
|
||||
feature: IEncodeFeature,
|
||||
featureIdx: number,
|
||||
vertex: number[],
|
||||
attributeIdx: number,
|
||||
) => {
|
||||
const { size } = feature;
|
||||
return Array.isArray(size) ? [size[0]] : [size as number];
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// point layer size;
|
||||
layer.styleAttributeService.registerStyleAttribute({
|
||||
name: 'normal',
|
||||
type: AttributeType.Attribute,
|
||||
descriptor: {
|
||||
name: 'a_Normal',
|
||||
buffer: {
|
||||
// give the WebGL driver a hint that this buffer may change
|
||||
usage: gl.STATIC_DRAW,
|
||||
data: [],
|
||||
type: gl.FLOAT,
|
||||
},
|
||||
size: 3,
|
||||
update: (
|
||||
feature: IEncodeFeature,
|
||||
featureIdx: number,
|
||||
vertex: number[],
|
||||
attributeIdx: number,
|
||||
normal: number[],
|
||||
) => {
|
||||
return normal;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
layer.styleAttributeService.registerStyleAttribute({
|
||||
name: 'miter',
|
||||
type: AttributeType.Attribute,
|
||||
descriptor: {
|
||||
name: 'a_Miter',
|
||||
buffer: {
|
||||
// give the WebGL driver a hint that this buffer may change
|
||||
usage: gl.DYNAMIC_DRAW,
|
||||
data: [],
|
||||
type: gl.FLOAT,
|
||||
},
|
||||
size: 1,
|
||||
update: (
|
||||
feature: IEncodeFeature,
|
||||
featureIdx: number,
|
||||
vertex: number[],
|
||||
attributeIdx: number,
|
||||
) => {
|
||||
return [vertex[4]];
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
layer.styleAttributeService.registerStyleAttribute({
|
||||
name: 'startPos',
|
||||
type: AttributeType.Attribute,
|
||||
descriptor: {
|
||||
name: 'a_StartPos',
|
||||
buffer: {
|
||||
// give the WebGL driver a hint that this buffer may change
|
||||
usage: gl.DYNAMIC_DRAW,
|
||||
data: [],
|
||||
type: gl.FLOAT,
|
||||
},
|
||||
size: 3,
|
||||
update: (
|
||||
feature: IEncodeFeature,
|
||||
featureIdx: number,
|
||||
vertex: number[],
|
||||
attributeIdx: number,
|
||||
) => {
|
||||
const coordinates = feature.coordinates as number[][];
|
||||
const coord = coordinates[0];
|
||||
return coord.length === 3 ? coord : [...coord, 0.0];
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
layer.styleAttributeService.registerStyleAttribute({
|
||||
name: 'distance',
|
||||
type: AttributeType.Attribute,
|
||||
descriptor: {
|
||||
name: 'a_Distance',
|
||||
buffer: {
|
||||
// give the WebGL driver a hint that this buffer may change
|
||||
usage: gl.DYNAMIC_DRAW,
|
||||
data: [],
|
||||
type: gl.FLOAT,
|
||||
},
|
||||
size: 1,
|
||||
update: (
|
||||
feature: IEncodeFeature,
|
||||
featureIdx: number,
|
||||
vertex: number[],
|
||||
attributeIdx: number,
|
||||
) => {
|
||||
return [vertex[3]];
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
layer.styleAttributeService.registerStyleAttribute({
|
||||
name: 'total_distance',
|
||||
type: AttributeType.Attribute,
|
||||
descriptor: {
|
||||
name: 'a_Total_Distance',
|
||||
buffer: {
|
||||
// give the WebGL driver a hint that this buffer may change
|
||||
usage: gl.DYNAMIC_DRAW,
|
||||
data: [],
|
||||
type: gl.FLOAT,
|
||||
},
|
||||
size: 1,
|
||||
update: (
|
||||
feature: IEncodeFeature,
|
||||
featureIdx: number,
|
||||
vertex: number[],
|
||||
attributeIdx: number,
|
||||
) => {
|
||||
return [vertex[5]];
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
|
@ -127,28 +127,5 @@ export default class LineLayer extends BaseLayer<IPointLayerStyleOptions> {
|
|||
},
|
||||
},
|
||||
});
|
||||
|
||||
layer.styleAttributeService.registerStyleAttribute({
|
||||
name: 'distance',
|
||||
type: AttributeType.Attribute,
|
||||
descriptor: {
|
||||
name: 'a_Distance',
|
||||
buffer: {
|
||||
// give the WebGL driver a hint that this buffer may change
|
||||
usage: gl.DYNAMIC_DRAW,
|
||||
data: [],
|
||||
type: gl.FLOAT,
|
||||
},
|
||||
size: 1,
|
||||
update: (
|
||||
feature: IEncodeFeature,
|
||||
featureIdx: number,
|
||||
vertex: number[],
|
||||
attributeIdx: number,
|
||||
) => {
|
||||
return [vertex[3]];
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
vec2 interpolate (vec2 source, vec2 target, float t) {
|
||||
// if the angularDist is PI, linear interpolation is applied. otherwise, use spherical interpolation
|
||||
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));
|
||||
}
|
||||
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 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;
|
||||
}
|
|
@ -6,6 +6,7 @@ attribute float a_Size;
|
|||
uniform mat4 u_ModelMatrix;
|
||||
uniform float segmentNumber;
|
||||
varying vec4 v_color;
|
||||
varying vec2 v_normal;
|
||||
|
||||
#pragma include "projection"
|
||||
|
||||
|
@ -39,7 +40,14 @@ vec2 getExtrusionOffset(vec2 line_clipspace, float offset_direction) {
|
|||
// rotate by 90 degrees
|
||||
dir_screenspace = vec2(-dir_screenspace.y, dir_screenspace.x);
|
||||
vec2 offset = dir_screenspace * offset_direction * a_Size / 2.0;
|
||||
return offset;
|
||||
return offset * vec2(1.0, -1.0);
|
||||
}
|
||||
vec2 getNormal(vec2 line_clipspace, float offset_direction) {
|
||||
// normalized direction of the line
|
||||
vec2 dir_screenspace = normalize(line_clipspace);
|
||||
// rotate by 90 degrees
|
||||
dir_screenspace = vec2(-dir_screenspace.y, dir_screenspace.x);
|
||||
return reverse_offset_normal(vec3(dir_screenspace,1.0)).xy * sign(offset_direction);
|
||||
}
|
||||
float getAngularDist (vec2 source, vec2 target) {
|
||||
vec2 delta = source - target;
|
||||
|
@ -77,12 +85,10 @@ void main() {
|
|||
float segmentRatio = getSegmentRatio(segmentIndex);
|
||||
float indexDir = mix(-1.0, 1.0, step(segmentIndex, 0.0));
|
||||
float nextSegmentRatio = getSegmentRatio(segmentIndex + indexDir);
|
||||
|
||||
vec4 curr = project_position(vec4(degrees(interpolate(source, target, angularDist, segmentRatio)), 0.0, 1.0));
|
||||
vec4 next = project_position(vec4(degrees(interpolate(source, target, angularDist, nextSegmentRatio)), 0.0, 1.0));
|
||||
|
||||
vec2 offset = getExtrusionOffset((next.xy - curr.xy) * indexDir, a_Position.y);
|
||||
|
||||
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, 0, 1.0));
|
||||
}
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
precision mediump float;
|
||||
uniform float u_opacity;
|
||||
varying vec4 v_color;
|
||||
uniform float u_blur : 0.90;
|
||||
varying vec2 v_normal;
|
||||
|
||||
void main() {
|
||||
|
||||
gl_FragColor = v_color;
|
||||
gl_FragColor.a = v_color.a * u_opacity;
|
||||
float blur = 1.- smoothstep(u_blur, 1., length(v_normal.xy));
|
||||
gl_FragColor.a *= (blur * u_opacity);
|
||||
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ attribute float a_Size;
|
|||
uniform mat4 u_ModelMatrix;
|
||||
uniform float segmentNumber;
|
||||
varying vec4 v_color;
|
||||
varying vec2 v_normal;
|
||||
#pragma include "projection"
|
||||
|
||||
float maps (float value, float start1, float stop1, float start2, float stop2) {
|
||||
|
@ -44,7 +45,13 @@ vec2 getExtrusionOffset(vec2 line_clipspace, float offset_direction) {
|
|||
|
||||
return offset;
|
||||
}
|
||||
|
||||
vec2 getNormal(vec2 line_clipspace, float offset_direction) {
|
||||
// normalized direction of the line
|
||||
vec2 dir_screenspace = normalize(line_clipspace);
|
||||
// rotate by 90 degrees
|
||||
dir_screenspace = vec2(-dir_screenspace.y, dir_screenspace.x);
|
||||
return reverse_offset_normal(vec3(dir_screenspace,1.0)).xy * sign(offset_direction);
|
||||
}
|
||||
|
||||
void main() {
|
||||
v_color = a_Color;
|
||||
|
@ -58,9 +65,8 @@ void main() {
|
|||
vec3 curr = getPos(source, target, segmentRatio);
|
||||
vec3 next = getPos(source, target, nextSegmentRatio);
|
||||
vec2 offset = getExtrusionOffset((next.xy - curr.xy) * indexDir, a_Position.y);
|
||||
v_normal = getNormal((next.xy - curr.xy) * indexDir, a_Position.y);
|
||||
|
||||
// vec4 project_pos = project_position(vec4(curr, 1.0));
|
||||
|
||||
gl_Position = project_common_position_to_clipspace(vec4(curr.xy + project_pixel(offset), curr.z, 1.0));
|
||||
gl_Position = project_common_position_to_clipspace(vec4(curr.xy + project_pixel(offset), curr.z, 1.0));
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,67 @@
|
|||
precision mediump float;
|
||||
attribute vec4 a_Color;
|
||||
attribute vec3 a_Position;
|
||||
attribute vec4 a_Instance;
|
||||
attribute float a_Size;
|
||||
uniform mat4 u_ModelMatrix;
|
||||
uniform float segmentNumber;
|
||||
varying vec4 v_color;
|
||||
varying vec2 v_normal;
|
||||
#pragma include "projection"
|
||||
|
||||
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 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 getSegmentRatio(float index) {
|
||||
return smoothstep(0.0, 1.0, index / (segmentNumber - 1.));
|
||||
}
|
||||
vec2 interpolate (vec2 source, vec2 target, float t) {
|
||||
// if the angularDist is PI, linear interpolation is applied. otherwise, use spherical interpolation
|
||||
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));
|
||||
}
|
||||
vec2 getExtrusionOffset(vec2 line_clipspace, float offset_direction) {
|
||||
// normalized direction of the line
|
||||
vec2 dir_screenspace = normalize(line_clipspace);
|
||||
// rotate by 90 degrees
|
||||
dir_screenspace = vec2(-dir_screenspace.y, dir_screenspace.x);
|
||||
vec2 offset = dir_screenspace * offset_direction * a_Size / 2.0;
|
||||
return offset * vec2(1.0, -1.0);
|
||||
}
|
||||
vec2 getNormal(vec2 line_clipspace, float offset_direction) {
|
||||
// normalized direction of the line
|
||||
vec2 dir_screenspace = normalize(line_clipspace);
|
||||
// rotate by 90 degrees
|
||||
dir_screenspace = vec2(-dir_screenspace.y, dir_screenspace.x);
|
||||
return reverse_offset_normal(vec3(dir_screenspace,1.0)).xy * sign(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);
|
||||
|
||||
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));
|
||||
|
||||
gl_Position = project_common_position_to_clipspace(vec4(curr.xy + offset, 0, 1.0));
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
uniform float u_blur : 0.9;
|
||||
uniform float u_opacity : 1.0;
|
||||
uniform float u_dash_offset : 0.0;
|
||||
uniform float u_dash_ratio : 0.1;
|
||||
varying vec4 v_color;
|
||||
varying vec2 v_normal;
|
||||
|
||||
varying float v_distance_ratio;
|
||||
varying vec2 v_dash_array;
|
||||
void main() {
|
||||
gl_FragColor = v_color;
|
||||
// gl_FragColor.a = v_distance_ratio;
|
||||
// anti-alias
|
||||
float blur = 1.- smoothstep(u_blur, 1., length(v_normal.xy)) * u_opacity;
|
||||
// gl_FragColor.a *= blur * ceil(mod(v_distance_ratio, v_dash_array.x) - v_dash_array.y);
|
||||
gl_FragColor.a *= blur * (1.0- step(v_dash_array.x, mod(v_distance_ratio, v_dash_array.x +v_dash_array.y)));
|
||||
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
|
||||
attribute float a_Miter;
|
||||
attribute vec4 a_Color;
|
||||
attribute float a_Size;
|
||||
attribute vec3 a_Normal;
|
||||
attribute float a_Total_Distance;
|
||||
attribute vec3 a_Position;
|
||||
attribute float a_Distance;
|
||||
uniform mat4 u_ModelMatrix;
|
||||
uniform vec2 u_dash_array: [10.0, 5.];
|
||||
uniform float u_dash_offset: 0;
|
||||
|
||||
varying vec4 v_color;
|
||||
varying vec2 v_dash_array;
|
||||
varying vec2 v_normal;
|
||||
|
||||
varying float v_distance_ratio;
|
||||
|
||||
#pragma include "projection"
|
||||
void main() {
|
||||
|
||||
v_distance_ratio = a_Distance / a_Total_Distance;
|
||||
|
||||
v_dash_array = pow(2.0, 20.0 - u_Zoom) * u_dash_array / a_Total_Distance;
|
||||
|
||||
v_normal = vec2(reverse_offset_normal(a_Normal) * sign(a_Miter));
|
||||
v_color = a_Color;
|
||||
vec3 size = a_Miter * a_Size * reverse_offset_normal(a_Normal); //v_normal * vec3(1., -1., 1.0);
|
||||
vec2 offset = project_pixel(size.xy);
|
||||
vec4 project_pos = project_position(vec4(a_Position.xy, 0, 1.0));
|
||||
gl_Position = project_common_position_to_clipspace(vec4(project_pos.xy + offset, 0, 1.0));
|
||||
}
|
|
@ -1,9 +1,16 @@
|
|||
uniform float u_blur : 0.99;
|
||||
uniform float u_blur : 0.9;
|
||||
uniform float u_opacity : 1.0;
|
||||
uniform float u_dash_offset : 0.0;
|
||||
uniform float u_dash_ratio : 0.0;
|
||||
varying vec4 v_color;
|
||||
varying vec3 v_normal;
|
||||
varying vec2 v_normal;
|
||||
|
||||
varying float v_distance_ratio;
|
||||
varying float v_dash_array;
|
||||
void main() {
|
||||
gl_FragColor = v_color;
|
||||
// anti-alias
|
||||
float blur = smoothstep(u_blur, 1., length(v_normal.xy));
|
||||
float blur = 1.- smoothstep(u_blur, 1., length(v_normal.xy)) * u_opacity;
|
||||
gl_FragColor.a *= blur;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -2,22 +2,17 @@
|
|||
attribute float a_Miter;
|
||||
attribute vec4 a_Color;
|
||||
attribute float a_Size;
|
||||
attribute float a_Distance;
|
||||
attribute float a_dash_array;
|
||||
attribute float a_total_distance;
|
||||
attribute vec3 a_Normal;
|
||||
attribute vec3 a_Position;
|
||||
uniform mat4 u_ModelMatrix;
|
||||
|
||||
#pragma include "projection"
|
||||
varying vec4 v_color;
|
||||
varying float v_dash_array;
|
||||
varying vec3 v_normal;
|
||||
|
||||
#pragma include "projection"
|
||||
varying vec2 v_normal;
|
||||
void main() {
|
||||
v_normal = a_Normal;
|
||||
v_normal = vec2(reverse_offset_normal(a_Normal) * sign(a_Miter));
|
||||
v_color = a_Color;
|
||||
vec3 size = a_Miter * a_Size * v_normal;
|
||||
vec3 size = a_Miter * a_Size * reverse_offset_normal(a_Normal); //v_normal * vec3(1., -1., 1.0);
|
||||
vec2 offset = project_pixel(size.xy);
|
||||
vec4 project_pos = project_position(vec4(a_Position.xy, 0, 1.0));
|
||||
gl_Position = project_common_position_to_clipspace(vec4(project_pos.xy + offset, 0, 1.0));
|
||||
|
|
|
@ -85,16 +85,14 @@ export default class DataMappingPlugin implements ILayerPlugin {
|
|||
if (!attribute.scale) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const scalers = attribute?.scale?.scalers || [];
|
||||
const params: unknown[] = [];
|
||||
|
||||
scalers.forEach(({ field }) => {
|
||||
if (record[field]) {
|
||||
if (record.hasOwnProperty(field) || attribute.scale?.type ==='variable') {
|
||||
params.push(record[field]);
|
||||
}
|
||||
});
|
||||
|
||||
return attribute.mapping ? attribute.mapping(params) : [];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import {
|
|||
ILayerPlugin,
|
||||
ILogService,
|
||||
IScale,
|
||||
IScaleOptions,
|
||||
IStyleAttribute,
|
||||
IStyleScale,
|
||||
lazyInject,
|
||||
|
@ -47,14 +48,18 @@ export default class FeatureScalePlugin implements ILayerPlugin {
|
|||
[field: string]: IStyleScale;
|
||||
} = {};
|
||||
|
||||
private scaleOptions: IScaleOptions = {}
|
||||
|
||||
public apply(layer: ILayer) {
|
||||
layer.hooks.init.tap('FeatureScalePlugin', () => {
|
||||
this.scaleOptions = layer.getScaleOptions();
|
||||
const attributes = layer.styleAttributeService.getLayerStyleAttributes();
|
||||
const { dataArray } = layer.getSource().data;
|
||||
this.caculateScalesForAttributes(attributes || [], dataArray);
|
||||
});
|
||||
|
||||
layer.hooks.beforeRender.tap('FeatureScalePlugin', () => {
|
||||
this.scaleOptions = layer.getScaleOptions();
|
||||
const attributes = layer.styleAttributeService.getLayerStyleAttributes();
|
||||
if (attributes) {
|
||||
const { dataArray } = layer.getSource().data;
|
||||
|
@ -154,15 +159,13 @@ export default class FeatureScalePlugin implements ILayerPlugin {
|
|||
|
||||
private createScale(field: string, data?: IParseDataItem[]): IStyleScale {
|
||||
// 首先查找全局默认配置例如 color
|
||||
const scaleOption: IScale | undefined = this.configService.getConfig()?.scales?.[field];
|
||||
|
||||
const scaleOption: IScale | undefined = this.scaleOptions[field];
|
||||
const styleScale: IStyleScale = {
|
||||
field,
|
||||
scale: undefined,
|
||||
type: StyleScaleType.VARIABLE,
|
||||
option: scaleOption,
|
||||
};
|
||||
|
||||
if (!data || !data.length) {
|
||||
|
||||
if (scaleOption && scaleOption.type) {
|
||||
|
@ -214,6 +217,8 @@ export default class FeatureScalePlugin implements ILayerPlugin {
|
|||
cfg.domain = extent(values);
|
||||
} else if (type === ScaleTypes.CAT) {
|
||||
cfg.domain = uniq(values);
|
||||
} else if(type === ScaleTypes.QUANTILE) {
|
||||
cfg.domain = values
|
||||
}
|
||||
return cfg;
|
||||
}
|
||||
|
|
|
@ -76,13 +76,6 @@ export default class MultiPassRendererPlugin implements ILayerPlugin {
|
|||
// 渲染前根据 viewport 调整 FBO size
|
||||
const { width, height } = this.rendererService.getViewportSize();
|
||||
layer.multiPassRenderer.resize(width, height);
|
||||
} else {
|
||||
// 未开启 MultiPassRenderer,由于没有 ClearPass,渲染前需要手动 clear
|
||||
this.rendererService.clear({
|
||||
color: [0, 0, 0, 0],
|
||||
depth: 1,
|
||||
framebuffer: null,
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -99,7 +92,7 @@ export default class MultiPassRendererPlugin implements ILayerPlugin {
|
|||
const { enablePicking, enableTAA } = layer.getStyleOptions();
|
||||
|
||||
// clear first
|
||||
multiPassRenderer.add(new ClearPass());
|
||||
// multiPassRenderer.add(new ClearPass());
|
||||
|
||||
// picking pass if enabled
|
||||
if (enablePicking) {
|
||||
|
|
|
@ -11,6 +11,7 @@ uniform float u_stroke_width : 1;
|
|||
varying float v_size;
|
||||
|
||||
#pragma include "projection"
|
||||
|
||||
void main() {
|
||||
v_color = a_Color;
|
||||
v_uv = a_Uv;
|
||||
|
@ -18,6 +19,6 @@ void main() {
|
|||
v_size = a_Size;
|
||||
|
||||
gl_Position = project_common_position_to_clipspace(vec4(project_pos.xyz, 1.0));
|
||||
gl_PointSize = a_Size + u_stroke_width;
|
||||
gl_PointSize = a_Size * 2.0 * u_DevicePixelRatio;
|
||||
|
||||
}
|
||||
|
|
|
@ -48,9 +48,10 @@ function addNext(
|
|||
}
|
||||
|
||||
function lineSegmentDistance(end: vec2, start: vec2) {
|
||||
const dx = start[0] - end[0];
|
||||
const dy = start[1] - end[1];
|
||||
// const dz = start[2] - end[2];
|
||||
const a1 = aProjectFlat([start[0], start[1]]);
|
||||
const b1 = aProjectFlat([end[0], end[1]]);
|
||||
const dx = a1[0] - b1[0];
|
||||
const dy = a1[1] - b1[1];
|
||||
return Math.sqrt(dx * dx + dy * dy);
|
||||
}
|
||||
|
||||
|
@ -212,12 +213,14 @@ export default function(
|
|||
}
|
||||
const pickData = [];
|
||||
for (let i = 0; i < miters.length; i++) {
|
||||
const totalDistance = attrDistance[attrDistance.length - 1];
|
||||
pickData.push(
|
||||
attrPos[i * 3],
|
||||
attrPos[i * 3 + 1],
|
||||
attrPos[i * 3 + 1],
|
||||
attrDistance[i],
|
||||
miters[i],
|
||||
totalDistance,
|
||||
);
|
||||
}
|
||||
return {
|
||||
|
|
|
@ -10,6 +10,7 @@ import {
|
|||
IMapService,
|
||||
IPoint,
|
||||
IViewport,
|
||||
MapServiceEvent,
|
||||
MapType,
|
||||
TYPES,
|
||||
} from '@l7/core';
|
||||
|
@ -32,8 +33,9 @@ export default class AMapService implements IMapService {
|
|||
|
||||
@inject(TYPES.ICoordinateSystemService)
|
||||
private readonly coordinateSystemService: ICoordinateSystemService;
|
||||
@inject(TYPES.IEventEmitter)
|
||||
private eventEmitter: IEventEmitter;
|
||||
private markerContainer: HTMLElement;
|
||||
|
||||
private $mapContainer: HTMLElement | null;
|
||||
private $jsapi: HTMLScriptElement;
|
||||
|
||||
|
@ -57,9 +59,12 @@ export default class AMapService implements IMapService {
|
|||
|
||||
// map event
|
||||
public on(type: string, handle: (...args: any[]) => void): void {
|
||||
this.map.on(type, handle);
|
||||
if (MapServiceEvent.indexOf(type) !== -1) {
|
||||
this.eventEmitter.on(type, handle);
|
||||
} else {
|
||||
this.map.on(type, handle);
|
||||
}
|
||||
}
|
||||
|
||||
public off(type: string, handle: (...args: any[]) => void): void {
|
||||
this.map.off(type, handle);
|
||||
}
|
||||
|
@ -179,6 +184,8 @@ export default class AMapService implements IMapService {
|
|||
|
||||
this.$mapContainer = document.getElementById(id);
|
||||
|
||||
// this.eventEmitter = container.get(TYPES.IEventEmitter);
|
||||
|
||||
// tslint:disable-next-line:typedef
|
||||
await new Promise((resolve) => {
|
||||
// 异步加载高德地图
|
||||
|
@ -194,6 +201,7 @@ export default class AMapService implements IMapService {
|
|||
|
||||
// 监听地图相机时间
|
||||
this.map.on('camerachange', this.handleCameraChanged);
|
||||
this.emit('mapload');
|
||||
resolve();
|
||||
};
|
||||
|
||||
|
@ -206,8 +214,16 @@ export default class AMapService implements IMapService {
|
|||
|
||||
this.viewport = new Viewport();
|
||||
}
|
||||
public emit(name: string, ...args: any[]) {
|
||||
this.eventEmitter.emit(name, ...args);
|
||||
}
|
||||
|
||||
public once(name: string, ...args: any[]) {
|
||||
this.eventEmitter.once(name, ...args);
|
||||
}
|
||||
|
||||
public destroy() {
|
||||
this.eventEmitter.removeAllListeners();
|
||||
if (this.map) {
|
||||
this.map.destroy();
|
||||
document.head.removeChild(this.$jsapi);
|
||||
|
@ -253,15 +269,15 @@ export default class AMapService implements IMapService {
|
|||
});
|
||||
|
||||
// set coordinate system
|
||||
if (this.viewport.getZoom() > LNGLAT_OFFSET_ZOOM_THRESHOLD) {
|
||||
// TODO:偏移坐标系高德地图不支持 pith bear 同步
|
||||
this.coordinateSystemService.setCoordinateSystem(
|
||||
CoordinateSystem.P20_OFFSET,
|
||||
);
|
||||
} else {
|
||||
this.coordinateSystemService.setCoordinateSystem(CoordinateSystem.P20);
|
||||
}
|
||||
|
||||
// if (this.viewport.getZoom() > LNGLAT_OFFSET_ZOOM_THRESHOLD) {
|
||||
// // TODO:偏移坐标系高德地图不支持 pith bear 同步
|
||||
// this.coordinateSystemService.setCoordinateSystem(
|
||||
// CoordinateSystem.P20_OFFSET,
|
||||
// );
|
||||
// } else {
|
||||
// this.coordinateSystemService.setCoordinateSystem(CoordinateSystem.P20);
|
||||
// }
|
||||
this.coordinateSystemService.setCoordinateSystem(CoordinateSystem.P20);
|
||||
this.cameraChangedCallback(this.viewport);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
export const MapTheme: {
|
||||
[key: string]: any;
|
||||
} = {
|
||||
dark: 'amap://styles/ba3e9759545cd618392ef073c0dfda8c?isPublic=true',
|
||||
dark: 'amap://styles/2a09079c3daac9420ee53b67307a8006?isPublic=true',
|
||||
light: 'amap://styles/1fd9f8ef9751298f11f5c56968312c70?isPublic=true',
|
||||
normal: 'amap://styles/12db649ba3493333b098127ed892c0cb?isPublic=true',
|
||||
};
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
import {
|
||||
Bounds,
|
||||
container,
|
||||
CoordinateSystem,
|
||||
ICoordinateSystemService,
|
||||
ILngLat,
|
||||
IMapConfig,
|
||||
IMapService,
|
||||
IPoint,
|
||||
IViewport,
|
||||
MapType,
|
||||
TYPES,
|
||||
} from '@l7/core';
|
||||
import { DOM } from '@l7/utils';
|
||||
import { inject, injectable } from 'inversify';
|
||||
import { IAMapEvent, IAMapInstance } from '../typings/index';
|
||||
|
||||
@injectable()
|
||||
export default class MapService<MapInstance> implements IMapService {
|
||||
public map: MapInstance;
|
||||
|
||||
@inject(TYPES.ICoordinateSystemService)
|
||||
protected readonly coordinateSystemService: ICoordinateSystemService;
|
||||
@inject(TYPES.IEventEmitter)
|
||||
protected eventEmitter: IEventEmitter;
|
||||
protected markerContainer: HTMLElement;
|
||||
protected $mapContainer: HTMLElement | null;
|
||||
|
||||
private cameraChangedCallback: (viewport: IViewport) => void;
|
||||
|
||||
public getMarkerContainer(): HTMLElement {
|
||||
return this.markerContainer;
|
||||
}
|
||||
}
|
|
@ -10,6 +10,7 @@ import {
|
|||
IMapService,
|
||||
IPoint,
|
||||
IViewport,
|
||||
MapServiceEvent,
|
||||
MapType,
|
||||
TYPES,
|
||||
} from '@l7/core';
|
||||
|
@ -37,6 +38,9 @@ export default class MapboxService implements IMapService {
|
|||
public map: Map & IMapboxInstance;
|
||||
@inject(TYPES.ICoordinateSystemService)
|
||||
private readonly coordinateSystemService: ICoordinateSystemService;
|
||||
|
||||
@inject(TYPES.IEventEmitter)
|
||||
private eventEmitter: IEventEmitter;
|
||||
private viewport: Viewport;
|
||||
private markerContainer: HTMLElement;
|
||||
private cameraChangedCallback: (viewport: IViewport) => void;
|
||||
|
@ -55,7 +59,12 @@ export default class MapboxService implements IMapService {
|
|||
|
||||
// map event
|
||||
public on(type: string, handle: (...args: any[]) => void): void {
|
||||
this.map.on(EventMap[type] || type, handle);
|
||||
if (MapServiceEvent.indexOf('mapload') !== -1) {
|
||||
this.eventEmitter.on(type, handle);
|
||||
} else {
|
||||
// 统一事件名称
|
||||
this.map.on(EventMap[type] || type, handle);
|
||||
}
|
||||
}
|
||||
public off(type: string, handle: (...args: any[]) => void): void {
|
||||
this.map.off(EventMap[type] || type, handle);
|
||||
|
@ -183,6 +192,7 @@ export default class MapboxService implements IMapService {
|
|||
attributionControl,
|
||||
...rest,
|
||||
});
|
||||
this.map.on('load', this.handleCameraChanged);
|
||||
this.map.on('move', this.handleCameraChanged);
|
||||
|
||||
// 不同于高德地图,需要手动触发首次渲染
|
||||
|
@ -197,12 +207,19 @@ export default class MapboxService implements IMapService {
|
|||
}
|
||||
|
||||
public destroy() {
|
||||
this.eventEmitter.removeAllListeners();
|
||||
if (this.map) {
|
||||
this.map.remove();
|
||||
document.head.removeChild(this.$link);
|
||||
this.$mapContainer = null;
|
||||
}
|
||||
}
|
||||
public emit(name: string, ...args: any[]) {
|
||||
this.eventEmitter.emit(name, ...args);
|
||||
}
|
||||
public once(name: string, ...args: any[]) {
|
||||
this.eventEmitter.once(name, ...args);
|
||||
}
|
||||
|
||||
public getMapContainer() {
|
||||
return this.$mapContainer;
|
||||
|
@ -242,6 +259,7 @@ export default class MapboxService implements IMapService {
|
|||
|
||||
this.cameraChangedCallback(this.viewport);
|
||||
};
|
||||
|
||||
private removeLogoControl(): void {
|
||||
// @ts-ignore
|
||||
const controls = this.map._controls as IControl[];
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
export const MapTheme: {
|
||||
[key: string]: any;
|
||||
} = {
|
||||
light: 'mapbox://styles/mapbox/light-v10',
|
||||
dark: 'mapbox://styles/mapbox/dark-v10',
|
||||
light: 'mapbox://styles/zcxduo/ck233y3ru1di71cnulo9jdg2v',
|
||||
dark: 'mapbox://styles/zcxduo/ck241p6413s0b1cpayzldv7x7',
|
||||
normal: 'mapbox://styles/zcxduo/ck2mzfaem0vdw1covi2yy793s',
|
||||
};
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
/// <reference path="../../../node_modules/@types/amap-js-api/index.d.ts" />
|
||||
/// <reference path="../../../node_modules/eventemitter3/index.d.ts" />
|
||||
|
||||
import { IControl } from 'mapbox-gl';
|
||||
|
||||
|
@ -29,3 +30,22 @@ interface IMapboxInstance {
|
|||
height: number;
|
||||
};
|
||||
}
|
||||
interface IEventEmitter<EventTypes extends string | symbol = string | symbol> {
|
||||
emit(event: EventTypes, ...args: any[]): boolean;
|
||||
/**
|
||||
* Add a listener for a given event.
|
||||
*/
|
||||
on(event: EventTypes, handle: (...args: any[]) => void, context?: any): this;
|
||||
|
||||
off(
|
||||
event: EventTypes,
|
||||
handle: (...args: any[]) => void,
|
||||
context?: any,
|
||||
once?: boolean,
|
||||
): this;
|
||||
|
||||
/**
|
||||
* Remove all listeners, or those of the specified event.
|
||||
*/
|
||||
removeAllListeners(event?: EventTypes): this;
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
"@l7/core": "^0.0.1",
|
||||
"@l7/maps": "^0.0.1",
|
||||
"@l7/renderer": "^0.0.1",
|
||||
"@l7/component": "^0.0.1",
|
||||
"mapbox-gl": "^1.2.1",
|
||||
"inversify": "^5.0.1",
|
||||
"inversify-inject-decorators": "^3.1.0",
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import { Logo } from '@l7/component';
|
||||
import {
|
||||
Bounds,
|
||||
container,
|
||||
|
@ -6,6 +7,7 @@ import {
|
|||
IIconService,
|
||||
IImage,
|
||||
ILayer,
|
||||
ILayerService,
|
||||
ILngLat,
|
||||
IMapConfig,
|
||||
IMapService,
|
||||
|
@ -68,6 +70,7 @@ class Scene {
|
|||
private sceneService: ISceneService;
|
||||
private mapService: IMapService;
|
||||
private controlService: IControlService;
|
||||
private layerService: ILayerService;
|
||||
|
||||
private iconService: IIconService;
|
||||
|
||||
|
@ -103,8 +106,11 @@ class Scene {
|
|||
this.mapService = container.get<IMapService>(TYPES.IMapService);
|
||||
this.iconService = container.get<IIconService>(TYPES.IIconService);
|
||||
this.controlService = container.get<IControlService>(TYPES.IControlService);
|
||||
this.layerService = container.get<ILayerService>(TYPES.ILayerService);
|
||||
mapType = this.mapService.getType();
|
||||
this.render();
|
||||
// 初始化 scene
|
||||
|
||||
this.init();
|
||||
}
|
||||
|
||||
public getMapService(): IMapService {
|
||||
|
@ -120,6 +126,18 @@ class Scene {
|
|||
this.sceneService.addLayer(layer);
|
||||
}
|
||||
|
||||
public getLayers(): ILayer[] {
|
||||
return this.layerService.getLayers();
|
||||
}
|
||||
|
||||
public getLayer(id: string): ILayer | undefined {
|
||||
return this.layerService.getLayer(id);
|
||||
}
|
||||
|
||||
public removeLayer(layer: ILayer): void {
|
||||
this.layerService.remove(layer);
|
||||
}
|
||||
|
||||
public render(): void {
|
||||
this.sceneService.render();
|
||||
}
|
||||
|
@ -139,7 +157,13 @@ class Scene {
|
|||
|
||||
// map control method
|
||||
public addControl(ctr: IControl) {
|
||||
this.controlService.addControl(ctr, this.mapService);
|
||||
if (this.mapService.map) {
|
||||
this.controlService.addControl(ctr, this.mapService);
|
||||
} else {
|
||||
this.mapService.once('mapload', () => {
|
||||
this.controlService.addControl(ctr, this.mapService);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public removeControl(ctr: IControl) {
|
||||
|
@ -241,6 +265,15 @@ class Scene {
|
|||
// TODO: 清理其他 Service 例如 IconService
|
||||
}
|
||||
|
||||
private init(): void {
|
||||
this.initControl();
|
||||
this.render();
|
||||
}
|
||||
|
||||
private initControl(): void {
|
||||
this.addControl(new Logo());
|
||||
}
|
||||
|
||||
// 资源管理
|
||||
}
|
||||
|
||||
|
|
|
@ -19,8 +19,8 @@ export function aggregatorToGrid(data: IParserData, option: ITransform) {
|
|||
const { gridHash, gridOffset } = _pointsGridHash(dataArray, size);
|
||||
const layerData = _getGridLayerDataFromGridHash(gridHash, gridOffset, option);
|
||||
return {
|
||||
yOffset: gridOffset.yOffset / 1.8,
|
||||
xOffset: gridOffset.xOffset / 1.8,
|
||||
yOffset: gridOffset.yOffset / 1.6,
|
||||
xOffset: gridOffset.xOffset / 1.6,
|
||||
radius: gridOffset.xOffset,
|
||||
dataArray: layerData,
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue