feat(component): add layer control

This commit is contained in:
thinkinggis 2019-11-10 21:20:26 +08:00
parent 26c3cca511
commit 0e000f5715
93 changed files with 2368 additions and 248 deletions

View File

@ -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

View File

@ -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);
});

View File

@ -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);
});

View File

@ -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": "世界电厂热力图"
}
]
}

View File

@ -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);
});

View File

@ -1,4 +1,4 @@
---
title: 热力图
order: 0
title: 网格热力图
order: 1
---

View File

@ -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);
});

View File

@ -0,0 +1,16 @@
{
"title": {
"zh": "网格热力图",
"en": "heatmap"
},
"demos": [
{
"filename": "grid.js",
"title": "网格热力图"
},
{
"filename": "world.js",
"title": "世界电厂热力图"
}
]
}

View File

@ -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);
});

View File

@ -0,0 +1,4 @@
---
title: 热力图
order: 0
---

View File

@ -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);
})

View File

@ -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);
})

View File

@ -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);
})

View File

@ -0,0 +1,24 @@
{
"title": {
"zh": "弧线",
"en": "line"
},
"demos": [
{
"filename": "arc3d.js",
"title": "3D弧线"
},
{
"filename": "arcCircle.js",
"title": "大圆弧线"
},
{
"filename": "arc.js",
"title": "弧线"
}
]
}

View File

@ -0,0 +1,4 @@
---
title: 弧线
order: 1
---

View File

@ -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);

View File

@ -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);
});

View File

@ -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": "等值线"
}
]
}

View File

@ -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);
});

View File

@ -1,4 +1,4 @@
---
title: 线图层
order: 1
title: 路径
order: 0
---

View File

@ -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);
});

View File

@ -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);
});

View File

@ -0,0 +1,20 @@
{
"title": {
"zh": "线图层",
"en": "line"
},
"demos": [
{
"filename": "path.js",
"title": "路径"
},
{
"filename": "line.js",
"title": "等高线"
},
{
"filename": "line2.js",
"title": "等值线"
}
]
}

View File

@ -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);
});

View File

@ -0,0 +1,4 @@
---
title: 虚线
order: 0
---

View File

@ -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);
});

View File

@ -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": "气泡图 - 电厂装机量"
}
]
}

View File

@ -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')

View File

@ -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);
});

View File

@ -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);
});

View File

@ -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);
});

View File

@ -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);

View File

@ -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);

View File

@ -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底图组件"
}
]
}

View File

@ -0,0 +1,6 @@
---
title: 组件
order: 0
---

View File

@ -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');

View File

@ -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>) {

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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

View File

@ -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

View File

@ -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 };

View File

@ -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);

View File

@ -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) {

View File

@ -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;
}

View File

@ -46,6 +46,9 @@ export interface IScaleOption {
format?: () => any;
domain?: any[];
}
export interface IScaleOptions {
[key: string]: IScale;
}
export interface IStyleScale {
scale: any;
field: string;

View File

@ -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,
});
}
}

View File

@ -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'];
/**
*
*/

View File

@ -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(),
() => {

View File

@ -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);

View File

@ -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'];

View File

@ -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');
}

View File

@ -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) {

View File

@ -1,4 +1,5 @@
const TYPES = {
IEventEmitter: Symbol.for('IEventEmitter'),
ISceneService: Symbol.for('ISceneService'),
IGlobalConfigService: Symbol.for('IGlobalConfigService'),
ICameraService: Symbol.for('ICameraService'),

View File

@ -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;

View File

@ -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,

View File

@ -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 },
}),
];
}

View File

@ -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,

View File

@ -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;
}

View File

@ -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));
}

View File

@ -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;
}

View File

@ -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));
}

View File

@ -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,

View File

@ -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: {

View File

@ -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,
},
}),
);

View File

@ -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]];
},
},
});
}
}

View File

@ -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]];
},
},
});
}
}

View File

@ -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]];
},
},
});
}
}

View File

@ -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;
}

View File

@ -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));
}

View File

@ -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);
}

View File

@ -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));
}

View File

@ -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));
}

View File

@ -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)));
}

View File

@ -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));
}

View File

@ -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;
}
}

View File

@ -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));

View File

@ -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) : [];
}
}

View File

@ -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;
}

View File

@ -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) {

View File

@ -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;
}

View File

@ -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 {

View File

@ -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);
}
};

View File

@ -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',
};

34
packages/maps/src/map.ts Normal file
View File

@ -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;
}
}

View File

@ -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[];

View File

@ -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',
};

View File

@ -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;
}

View File

@ -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",

View File

@ -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());
}
// 资源管理
}

View File

@ -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,
};