mirror of https://gitee.com/antv-l7/antv-l7
docs: 官网文档新增 createModelData/updateModelData 方法的 demo、文档 (#1083)
* chore: update version 2.8.30 -> 2.8.31 * docs: 完善时间轴公交数据 demo * style: lint style * docs: 增加官网依赖配置 * feat: 增加公交时序数据 * docs: 为地形 demo 增加 LOD * style: lint style * docs: 修改公交站点时序数据的 demo * style: lint style * docs: 官网文档新增 createModelData/updateModelData 方法的 demo、文档
This commit is contained in:
parent
44bfbfe803
commit
a6e32892ac
|
@ -39,3 +39,50 @@ layer.setData(data);
|
|||
参数:
|
||||
|
||||
- type blend 类型 normal | additive | subtractive | max
|
||||
|
||||
### createModelData(data: any, option?: ISourceCFG)
|
||||
- data 原始数据
|
||||
- options 为可选参数,一般是解析数据的 parser,但是在某些特殊图层中还可能是其他的参数
|
||||
|
||||
```javascript
|
||||
const modelData = layer.createModelData(data); // data 为 GeoJson
|
||||
|
||||
const modelData = layer.createModelData(data, { // data 为 json 数据
|
||||
parser: {
|
||||
type: 'json',
|
||||
x: 'lng',
|
||||
y: 'lat',
|
||||
},
|
||||
});
|
||||
|
||||
const modelData = layer.createModelData([], { // 计算 planeGeometry 的 modelData
|
||||
widthSegments: 100, // planeGeometry 的顶点和 widthSegments/heightSegments 相关
|
||||
heightSegments: 100,
|
||||
});
|
||||
```
|
||||
|
||||
🌟 在计算某些图层的 modelData 的时候我们需要考虑对应的时机,如 planeGeometry 在加载地形的时候
|
||||
planeGeometry 的顶点位置和地形贴图相关,因此如果我们要计算实际地形顶点的模型数据,需要等待对应地形贴图加载完:
|
||||
|
||||
```javascript
|
||||
let modelData10 = null, modelData100 = null;
|
||||
layer.on('terrainImageLoaded', () => {
|
||||
modelData10 = layer.createModelData([], {
|
||||
widthSegments: 10,
|
||||
heightSegments: 10,
|
||||
});
|
||||
|
||||
modelData100 = layer.createModelData([], {
|
||||
widthSegments: 100,
|
||||
heightSegments: 100,
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
[在线案例](/zh/examples/geometry/geometry#terrain)
|
||||
### updateModelData(data: IAttrubuteAndElements)
|
||||
- data 是通过 createModelData 方法生成的图层的标准模型数据。
|
||||
|
||||
我们通过这个方法可以实时更新图层的模型数据。
|
||||
|
||||
[在线案例](/zh/examples/gallery/animate#timeline)
|
|
@ -48,6 +48,11 @@
|
|||
"filename": "box_select.js",
|
||||
"title": "框选要素",
|
||||
"screenshot":"https://gw.alipayobjects.com/mdn/rms_816329/afts/img/A*G1lyQ6e5gKAAAAAAAAAAAAAAARQnAQ"
|
||||
},
|
||||
{
|
||||
"filename": "timeline.js",
|
||||
"title": "公交站点时序数据",
|
||||
"screenshot":"https://gw.alipayobjects.com/mdn/rms_816329/afts/img/A*l2YoR56mK1EAAAAAAAAAAAAAARQnAQ"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -0,0 +1,178 @@
|
|||
import { Scene, PointLayer } from '@antv/l7';
|
||||
import { GaodeMap } from '@antv/l7-maps';
|
||||
let currentTimeKey = '0000';
|
||||
let timeKeys = [];
|
||||
let layer = null;
|
||||
const modelDatas = {};
|
||||
const parser = {
|
||||
parser: {
|
||||
type: 'json',
|
||||
x: 'o',
|
||||
y: 'a'
|
||||
}
|
||||
};
|
||||
insetDom();
|
||||
const scene = new Scene({
|
||||
id: 'map',
|
||||
map: new GaodeMap({
|
||||
center: [ 120.2, 36.1 ],
|
||||
zoom: 10,
|
||||
style: 'dark'
|
||||
})
|
||||
});
|
||||
scene.on('loaded', () => {
|
||||
setDragBar();
|
||||
|
||||
fetch(
|
||||
'https://gw.alipayobjects.com/os/bmw-prod/82d85bb6-db7c-4583-af26-35b11c7b2d0d.json'
|
||||
)
|
||||
.then(res => res.json())
|
||||
.then(originData => {
|
||||
timeKeys = Object.keys(originData);
|
||||
|
||||
layer = new PointLayer({})
|
||||
.source(originData[currentTimeKey], parser)
|
||||
.shape('simple')
|
||||
.size('v', v => Math.sqrt(v) / 5)
|
||||
.color('v', [
|
||||
'#ffffb2',
|
||||
'#fed976',
|
||||
'#feb24c',
|
||||
'#fd8d3c',
|
||||
'#f03b20',
|
||||
'#bd0026'
|
||||
])
|
||||
.style({
|
||||
opacity: 0.6
|
||||
});
|
||||
|
||||
scene.addLayer(layer);
|
||||
|
||||
getModelDatas(originData);
|
||||
return '';
|
||||
});
|
||||
return '';
|
||||
});
|
||||
|
||||
|
||||
function getModelDatas(originData) {
|
||||
timeKeys.map(timeKey => {
|
||||
modelDatas[timeKey] = layer.createModelData(originData[timeKey], parser);
|
||||
return '';
|
||||
});
|
||||
}
|
||||
|
||||
function updateTime() {
|
||||
if (layer && scene) {
|
||||
layer.updateModelData(modelDatas[currentTimeKey]);
|
||||
scene.render();
|
||||
}
|
||||
}
|
||||
|
||||
function setDragBar() {
|
||||
const script = document.createElement('script');
|
||||
script.setAttribute('type', 'text/javascript');
|
||||
script.setAttribute('src', 'https://cdn.bootcss.com/jquery/2.1.1/jquery.min.js');
|
||||
script.async = true;
|
||||
document.head.appendChild(script);
|
||||
|
||||
script.onload = () => {
|
||||
const bar = document.getElementById('progress');
|
||||
const barWidth = bar.getBoundingClientRect().width;
|
||||
let tag = false,
|
||||
ox = 0,
|
||||
left = 0,
|
||||
bgleft = 0;
|
||||
const $ = window.$;
|
||||
$('.progress_btn').mousedown(function(e) {
|
||||
ox = e.pageX - left;
|
||||
tag = true;
|
||||
});
|
||||
|
||||
$(document).mouseup(function() { tag = false; });
|
||||
|
||||
$(document).mousemove(function(e) { // 鼠标移动
|
||||
if (tag) {
|
||||
left = e.pageX - ox;
|
||||
currentTimeKey = setText(left, barWidth);
|
||||
updateTime();
|
||||
}
|
||||
});
|
||||
|
||||
$('.progress_bg').click(function(e) { // 鼠标点击
|
||||
if (!tag) {
|
||||
bgleft = $('.progress_bg').offset().left;
|
||||
left = e.pageX - bgleft;
|
||||
currentTimeKey = setText(left, barWidth);
|
||||
updateTime();
|
||||
}
|
||||
});
|
||||
return '';
|
||||
};
|
||||
|
||||
|
||||
function setText(left, barWidth) {
|
||||
|
||||
if (left <= 0) {
|
||||
left = 0;
|
||||
} else if (left > barWidth) {
|
||||
left = barWidth;
|
||||
}
|
||||
const $ = window.$;
|
||||
$('.progress_btn').css('left', left);
|
||||
|
||||
const time = parseInt((left / barWidth) * 48);
|
||||
const timeText = getTimeKey(time, ':');
|
||||
const timeKey = getTimeKey(time, '');
|
||||
$('.text').html(timeText);
|
||||
return timeKey;
|
||||
}
|
||||
}
|
||||
|
||||
function insetDom() {
|
||||
const mapContrainer = document.getElementById('map');
|
||||
const wrap = document.createElement('div');
|
||||
wrap.id = 'progress';
|
||||
wrap.style.zIndex = 10;
|
||||
wrap.style.position = 'absolute';
|
||||
wrap.style.bottom = '50px';
|
||||
wrap.style.left = '10%';
|
||||
wrap.style.right = '10%';
|
||||
wrap.innerHTML = `
|
||||
<div class="progress_bg" style="
|
||||
height: 6px;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
background-color:#f2f2f2;">
|
||||
</div>
|
||||
<div class="progress_btn" style="
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border-radius: 10px;
|
||||
position: absolute;
|
||||
background:#ccc;
|
||||
left: 0px;
|
||||
margin-left: -10px; top:-9px;
|
||||
cursor: pointer;
|
||||
box-sizing:border-box;
|
||||
">
|
||||
<div class="text" style="transform:translateY(-30px); color: #fff;user-select:none">00:00</div>
|
||||
</div>
|
||||
`;
|
||||
mapContrainer.appendChild(wrap);
|
||||
}
|
||||
|
||||
function getTimeKey(time, text) {
|
||||
const half = Math.floor(time / 2);
|
||||
let res = '';
|
||||
if (half < 10) {
|
||||
res += '0';
|
||||
}
|
||||
if (time / 2 > half) {
|
||||
res += half + text + '30';
|
||||
} else {
|
||||
res += half + text + '00';
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
|
@ -11,7 +11,7 @@
|
|||
},
|
||||
{
|
||||
"filename": "terrain.js",
|
||||
"title": "自定义 3D 地形",
|
||||
"title": "自定义 3D 地形(LOD)",
|
||||
"screenshot": "https://gw.alipayobjects.com/mdn/rms_816329/afts/img/A*DIb4TaijKIAAAAAAAAAAAAAAARQnAQ"
|
||||
},
|
||||
{
|
||||
|
|
|
@ -11,6 +11,8 @@ const scene = new Scene({
|
|||
zoom: 14
|
||||
})
|
||||
});
|
||||
let currentZoom = 14,
|
||||
currentModelData = '100x100';
|
||||
|
||||
scene.on('loaded', () => {
|
||||
const layer = new GeometryLayer()
|
||||
|
@ -19,8 +21,8 @@ scene.on('loaded', () => {
|
|||
width: 0.074,
|
||||
height: 0.061,
|
||||
center: [ 120.1025, 30.2594 ],
|
||||
widthSegments: 200,
|
||||
heightSegments: 200,
|
||||
widthSegments: 100,
|
||||
heightSegments: 100,
|
||||
terrainClipHeight: 1,
|
||||
mapTexture:
|
||||
'https://gw.alipayobjects.com/mdn/rms_23a451/afts/img/A*gA0NRbuOF5cAAAAAAAAAAAAAARQnAQ',
|
||||
|
@ -36,4 +38,51 @@ scene.on('loaded', () => {
|
|||
}
|
||||
});
|
||||
scene.addLayer(layer);
|
||||
|
||||
let modelData10,
|
||||
modelData20 = null,
|
||||
modelData100;
|
||||
|
||||
layer.on('terrainImageLoaded', () => {
|
||||
|
||||
modelData10 = layer.createModelData([], {
|
||||
widthSegments: 10,
|
||||
heightSegments: 10
|
||||
});
|
||||
|
||||
modelData20 = layer.createModelData([], {
|
||||
widthSegments: 20,
|
||||
heightSegments: 20
|
||||
});
|
||||
|
||||
modelData100 = layer.createModelData([], {
|
||||
widthSegments: 100,
|
||||
heightSegments: 100
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
scene.on('zoom', ({ value }) => {
|
||||
const zoom = Math.floor(value);
|
||||
if (currentZoom !== zoom) {
|
||||
if (zoom > 13) {
|
||||
if (currentModelData !== '100x100') {
|
||||
layer.updateModelData(modelData100);
|
||||
currentModelData = '100x100';
|
||||
}
|
||||
} else if (zoom > 12) {
|
||||
if (currentModelData !== '20x20') {
|
||||
layer.updateModelData(modelData20);
|
||||
currentModelData = '20x20';
|
||||
}
|
||||
} else {
|
||||
if (currentModelData !== '10x10') {
|
||||
layer.updateModelData(modelData10);
|
||||
currentModelData = '10x10';
|
||||
}
|
||||
}
|
||||
currentZoom = zoom;
|
||||
}
|
||||
return '';
|
||||
});
|
||||
});
|
||||
|
|
|
@ -19,5 +19,4 @@ window.react = require('react');
|
|||
window.popmotion = require('popmotion');
|
||||
window.reactDom = require('react-dom');
|
||||
window.antd = require('antd');
|
||||
window.d3Dsv = require('d3-dsv');
|
||||
window.materialUI = require('@material-ui')
|
||||
window.materialUICore = require('@material-ui/core')
|
||||
|
|
|
@ -3,7 +3,6 @@ import { Scene, json } from '@antv/l7';
|
|||
import { PointLayer } from '@antv/l7-layers';
|
||||
import { GaodeMap } from '@antv/l7-maps';
|
||||
import * as React from 'react';
|
||||
import { csvParse } from 'd3-dsv';
|
||||
import { styled, withStyles } from '@material-ui/core/styles';
|
||||
import Slider from '@material-ui/core/Slider';
|
||||
|
||||
|
@ -13,7 +12,7 @@ export default class Demo extends React.Component {
|
|||
|
||||
constructor() {
|
||||
this.state = {
|
||||
currentYear: 50,
|
||||
currentYear: 24,
|
||||
modelDatas: undefined,
|
||||
};
|
||||
}
|
||||
|
@ -21,34 +20,18 @@ export default class Demo extends React.Component {
|
|||
this.scene.destroy();
|
||||
}
|
||||
|
||||
public getSortedData(dataList: { DateTime: string }[]) {
|
||||
const res = {},
|
||||
years = [];
|
||||
dataList.map((data) => {
|
||||
const { DateTime } = data;
|
||||
const year = DateTime.slice(0, 4);
|
||||
if (res[year]) {
|
||||
res[year].push({
|
||||
Latitude: Number(data.Latitude),
|
||||
Longitude: Number(data.Longitude),
|
||||
Depth: Number(data.Depth),
|
||||
Magnitude: Number(data.Magnitude),
|
||||
});
|
||||
} else {
|
||||
years.push(year);
|
||||
res[year] = [];
|
||||
res[year].push({
|
||||
Latitude: Number(data.Latitude),
|
||||
Longitude: Number(data.Longitude),
|
||||
Depth: Number(data.Depth),
|
||||
Magnitude: Number(data.Magnitude),
|
||||
});
|
||||
}
|
||||
});
|
||||
return {
|
||||
res,
|
||||
years,
|
||||
};
|
||||
public getTimeKey(time) {
|
||||
const half = Math.floor(time / 2);
|
||||
const res = '';
|
||||
if (half < 10) {
|
||||
res += '0';
|
||||
}
|
||||
if (time / 2 > half) {
|
||||
res += half + '30';
|
||||
} else {
|
||||
res += half + '00';
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
public getModelDatas(layer, sortedData, years, parser) {
|
||||
|
@ -62,50 +45,37 @@ export default class Demo extends React.Component {
|
|||
});
|
||||
}
|
||||
|
||||
public generateData(size) {
|
||||
let data = [];
|
||||
for (let i = 0; i < size; i++) {
|
||||
data.push({
|
||||
lng: Math.random() * 180,
|
||||
lat: Math.random() * 80 - 40,
|
||||
c: Math.random() > 0.5 ? '#f00' : '#ff0',
|
||||
});
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
public async componentDidMount() {
|
||||
const scene = new Scene({
|
||||
id: 'map',
|
||||
map: new GaodeMap({
|
||||
center: [-120, 36],
|
||||
center: [120.2, 36.1],
|
||||
pitch: 0,
|
||||
zoom: 6,
|
||||
zoom: 10,
|
||||
style: 'dark',
|
||||
}),
|
||||
});
|
||||
this.scene = scene;
|
||||
|
||||
// 公交出行需求量的时序数据
|
||||
scene.on('loaded', () => {
|
||||
fetch(
|
||||
'https://gw.alipayobjects.com/os/bmw-prod/6b15fe03-9d5b-4779-831d-ec30aa2e4738.csv',
|
||||
'https://gw.alipayobjects.com/os/bmw-prod/82d85bb6-db7c-4583-af26-35b11c7b2d0d.json',
|
||||
)
|
||||
.then((res) => res.text())
|
||||
.then((res) => {
|
||||
const originData = csvParse(res);
|
||||
const { res: sortedData, years } = this.getSortedData(originData);
|
||||
.then((res) => res.json())
|
||||
.then((originData) => {
|
||||
const times = Object.keys(originData);
|
||||
const parser = {
|
||||
parser: {
|
||||
type: 'json',
|
||||
x: 'Longitude',
|
||||
y: 'Latitude',
|
||||
x: 'o',
|
||||
y: 'a',
|
||||
},
|
||||
};
|
||||
|
||||
let layer = new PointLayer()
|
||||
.source(sortedData[years[0]], parser)
|
||||
let layer = new PointLayer({})
|
||||
.source(originData[times[0]], parser)
|
||||
.shape('simple')
|
||||
.size('Magnitude', (v) => Math.pow(v, 2))
|
||||
.color('Magnitude', [
|
||||
.size('v', (v) => Math.sqrt(v) / 5)
|
||||
.color('v', [
|
||||
'#ffffb2',
|
||||
'#fed976',
|
||||
'#feb24c',
|
||||
|
@ -114,20 +84,20 @@ export default class Demo extends React.Component {
|
|||
'#bd0026',
|
||||
])
|
||||
.style({
|
||||
opacity: 0.5,
|
||||
opacity: 0.6,
|
||||
});
|
||||
|
||||
scene.addLayer(layer);
|
||||
this.layer = layer;
|
||||
|
||||
this.getModelDatas(layer, sortedData, years, parser);
|
||||
this.getModelDatas(layer, originData, times, parser);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public timelinechange(time) {
|
||||
if (time !== this.state.currentYear) {
|
||||
this.layer.updateModelData(this.state.modelDatas[time]);
|
||||
this.layer.updateModelData(this.state.modelDatas[this.getTimeKey(time)]);
|
||||
this.scene.render();
|
||||
this.setState({
|
||||
currentYear: time,
|
||||
|
@ -149,9 +119,9 @@ export default class Demo extends React.Component {
|
|||
>
|
||||
{this.state.modelDatas !== undefined && (
|
||||
<RangeInput
|
||||
min={1988}
|
||||
max={2018}
|
||||
value={this?.state?.currentYear || 1988}
|
||||
min={0}
|
||||
max={48}
|
||||
value={this?.state?.currentYear || 0}
|
||||
onChange={(e) => this.timelinechange(e)}
|
||||
/>
|
||||
)}
|
||||
|
@ -162,7 +132,7 @@ export default class Demo extends React.Component {
|
|||
|
||||
const PositionContainer = styled('div')({
|
||||
position: 'absolute',
|
||||
zIndex: 1,
|
||||
zIndex: 2,
|
||||
bottom: '40px',
|
||||
width: '100%',
|
||||
display: 'flex',
|
||||
|
@ -173,12 +143,12 @@ const PositionContainer = styled('div')({
|
|||
const SliderInput = withStyles({
|
||||
root: {
|
||||
marginLeft: 12,
|
||||
width: '40%',
|
||||
width: '60%',
|
||||
},
|
||||
valueLabel: {
|
||||
'& span': {
|
||||
background: 'none',
|
||||
color: '#000',
|
||||
color: '#fff',
|
||||
},
|
||||
},
|
||||
})(Slider);
|
||||
|
@ -192,7 +162,19 @@ function RangeInput({ min, max, value, onChange }) {
|
|||
value={value}
|
||||
onChange={(event, newValue) => onChange(newValue)}
|
||||
valueLabelDisplay="auto"
|
||||
valueLabelFormat={(t) => t}
|
||||
valueLabelFormat={(t) => {
|
||||
const half = Math.floor(t / 2);
|
||||
const res = '';
|
||||
if (half < 10) {
|
||||
res += '0';
|
||||
}
|
||||
if (t / 2 > half) {
|
||||
res += half + ':30';
|
||||
} else {
|
||||
res += half + ':00';
|
||||
}
|
||||
return res;
|
||||
}}
|
||||
/>
|
||||
</PositionContainer>
|
||||
);
|
||||
|
|
Loading…
Reference in New Issue