docs: add source docs, fix mapbox marker

This commit is contained in:
thinkinggis 2019-12-10 20:11:26 +08:00
parent 3a02abfc3a
commit 6322779a78
21 changed files with 464 additions and 269 deletions

View File

@ -25,6 +25,14 @@ shape 支持
```
## source
点数据类型,根据经纬点绘制图形,目前支持三种数据结构
- [GeoJOSN]('../source/geojson/#point')
- [CSV]()
- [JSON](../source/json/#点数据)
**图片标注**
通过 `Scene.addImage()` 可以添加图片资源,

53
docs/api/source/csv.en.md Normal file
View File

@ -0,0 +1,53 @@
---
title: CSV
order: 3
---
L7 支持 CSV 以逗号分隔的 CSV 数据加载。
CSV 是文本数据结构,很难表达复杂的地理数据结构,因此 CSV 仅支持两种数据结构
- 点数据 需要指定经度,纬度坐标
- 线段,弧线数据 需要指定 起始点的 经度,纬度坐标
## parser
- type string 必选 json
- x string 点数据表示 经度
- y string 点数据表示 纬度
- x1 string 经度
- x2 string 纬度
### 点数据通过 CSV 加载
```javascript
layer.source(data, {
parser: {
type: 'csv',
x: 'lng',
y: 'lat',
},
});
```
[CSV 数据 demo 示例](../../../examples/point/bubble#scatter)
### 线段弧线数据通过 CSV 加载
```javascript
layer.source(
data,
{
parser:{
type:'csv',
x:'lng1',
y:'lat1' ,
x1:'lng1',
y1:'lat2' ,
}
}
})
```
[CSV 线段数据 demo 示例](../../../examples/gallery/basic#arcCircle)

53
docs/api/source/csv.zh.md Normal file
View File

@ -0,0 +1,53 @@
---
title: CSV
order: 3
---
L7 支持 CSV 以逗号分隔的 CSV 数据加载。
CSV 是文本数据结构,很难表达复杂的地理数据结构,因此 CSV 仅支持两种数据结构
- 点数据 需要指定经度,纬度坐标
- 线段,弧线数据 需要指定 起始点的 经度,纬度坐标
## parser
- type string 必选 json
- x string 点数据表示 经度
- y string 点数据表示 纬度
- x1 string 经度
- x2 string 纬度
### 点数据通过 CSV 加载
```javascript
layer.source(data, {
parser: {
type: 'csv',
x: 'lng',
y: 'lat',
},
});
```
[CSV 数据 demo 示例](../../../examples/point/bubble#scatter)
### 线段弧线数据通过 CSV 加载
```javascript
layer.source(
data,
{
parser:{
type:'csv',
x:'lng1',
y:'lat1' ,
x1:'lng1',
y1:'lat2' ,
}
}
})
```
[CSV 线段数据 demo 示例](../../../examples/gallery/basic#arcCircle)

View File

@ -0,0 +1,25 @@
---
title: Image
order: 4
---
Image 数据主要用于在地图根据经纬度范围添加图图片,不如一幅纸制地图扫描版你要放在地图显示。
## parser
- type: image
- extent: 图像的经纬度范围 [minlng, minlat,maxLng, maxLat]
根据图片的经纬度范围,将图片添加到地图上。
```javascript
layer.source(
'https://gw.alipayobjects.com/zos/rmsportal/FnHFeFklTzKDdUESRNDv.jpg',
{
parser: {
type: 'image',
extent: [121.168, 30.2828, 121.384, 30.4219],
},
},
);
```

View File

@ -0,0 +1,25 @@
---
title: Image
order: 4
---
Image 数据主要用于在地图根据经纬度范围添加图图片,不如一幅纸制地图扫描版你要放在地图显示。
## parser
- type: image
- extent: 图像的经纬度范围 [minlng, minlat,maxLng, maxLat]
根据图片的经纬度范围,将图片添加到地图上。
```javascript
layer.source(
'https://gw.alipayobjects.com/zos/rmsportal/FnHFeFklTzKDdUESRNDv.jpg',
{
parser: {
type: 'image',
extent: [121.168, 30.2828, 121.384, 30.4219],
},
},
);
```

View File

@ -1,6 +1,6 @@
---
title: JSON
order: 1
order: 2
---
GeoJSON 虽然是通用的的地理数据格式,在具体使用场景中,数据服务人员可能并不熟悉 GeoJON,或者没有生成 GeoJON 的工具, 因此 L7 对数据定义了 Parser 的概念,你的数据可以是任何格式,使用指定数据对应的地理信息字段即可。

View File

@ -1,6 +1,6 @@
---
title: JSON
order: 1
order: 2
---
GeoJSON 虽然是通用的的地理数据格式,在具体使用场景中,数据服务人员可能并不熟悉 GeoJON,或者没有生成 GeoJON 的工具, 因此 L7 对数据定义了 Parser 的概念,你的数据可以是任何格式,使用指定数据对应的地理信息字段即可。
@ -39,6 +39,8 @@ layer.source(data, {
});
```
[JOSN 数据 demo 示例](../../../examples/gallery/basic)
### 通用解析方式
可也解析任意复杂的点,线面

View File

@ -40,100 +40,25 @@ layer.source(data);
#### JSON
[JSON 数据格式解析](../json)
[JSON 数据格式解析](./json)
#### csv
点,线数据配置项同 json 数据类型
[CSV 数据格式解析](./csv)
```javascript
layer.source(data, {
parser: {
type: 'csv',
x: 'lng1',
y: 'lat1',
x1: 'lng1',
y1: 'lat2',
},
});
```
**栅格数据类型 **
栅格数据类型
#### image
根据图片的经纬度范围,将图片添加到地图上。  配置项
- type: image
- extent: 图像的经纬度范围 []
```javascript
layer.source(
'https://gw.alipayobjects.com/zos/rmsportal/FnHFeFklTzKDdUESRNDv.jpg',
{
parser: {
type: 'image',
extent: [121.168, 30.2828, 121.384, 30.4219],
},
},
);
```
#### raster
栅格数据类型,主要表示遥感数据类型 data 栅格数据的二维矩阵数据 parser 配置项
- type  raster
- width  数据宽度二维矩阵 columns
- height 数据高度
- min 数据最大值
- max 数据最小值
- extent 经纬度范围
```javascript
source(values, {
parser: {
type: 'raster',
width: n,
height: m,
min: 0,
max: 8000,
extent: [73.482190241, 3.82501784112, 135.106618732, 57.6300459963],
},
});
```
[Image 数据格式解析](./image)
### transforms
目前支持三种数据处理方法 mapgridhexagon transform 配置项
目前支持两种热力图使用的数据处理方法 gridhexagon transform 配置项
- type 数据处理类型
- tansform cfg  数据处理配置项
#### map
数据处理,支持自定义 callback 函数
- callback:function 回调函数
```javascript
layer.source(data, {
transforms: [
{
type: 'map',
callback: function(item) {
const [x, y] = item.coordinates;
item.lat = item.lat * 1;
item.lng = item.lng * 1;
item.v = item.v * 1;
item.coordinates = [x * 1, y * 1];
return item;
},
},
],
});
```
#### grid
生成方格网布局,根据数据字段统计,主要在网格热力图中使用
@ -163,4 +88,4 @@ layer.source(data, {
- type: 'hexagon',
- size: 网格半径
- field: 数据统计字段
- method:聚合方法   count,max,min,sum,mean5 个统计维度
- method:聚合方法   count,max,min,sum,mean 5 个统计维度

View File

@ -7,6 +7,13 @@ order: 0
source 地理数据处理模块主要包含数据解析parser),和数据处理(transform);
- data
- option
- cluster **boolean** 是否聚合
- clusterOption 聚合配置项
- parser 数据解析配置
- transforms 数据处理配置
### parser
不同数据类型处理成统一数据格式。矢量数据包括 GeoJON, CSVJson 等不同数据格式,栅格数据,包括 RasterImage 数据。将来还会支持瓦片格式数据。
@ -23,6 +30,14 @@ source 地理数据处理模块主要包含数据解析parser),和数据
## API
### cluster 可选 可以只设置 cluster
### clusterOption 可选
- radius 聚合半径 **number** default 40
- minZoom: 最小聚合缩放等级 **number** default 0
- maxZoom: 最大聚合缩放等级 **number** default 16
### parser
**配置项**
@ -40,100 +55,25 @@ layer.source(data);
#### JSON
[JSON 数据格式解析](../json)
[JSON 数据格式解析](./json)
#### csv
点,线数据配置项同 json 数据类型
[CSV 数据格式解析](./csv)
```javascript
layer.source(data, {
parser: {
type: 'csv',
x: 'lng1',
y: 'lat1',
x1: 'lng1',
y1: 'lat2',
},
});
```
**栅格数据类型 **
栅格数据类型
#### image
根据图片的经纬度范围,将图片添加到地图上。  配置项
- type: image
- extent: 图像的经纬度范围 []
```javascript
layer.source(
'https://gw.alipayobjects.com/zos/rmsportal/FnHFeFklTzKDdUESRNDv.jpg',
{
parser: {
type: 'image',
extent: [121.168, 30.2828, 121.384, 30.4219],
},
},
);
```
#### raster
栅格数据类型,主要表示遥感数据类型 data 栅格数据的二维矩阵数据 parser 配置项
- type  raster
- width  数据宽度二维矩阵 columns
- height 数据高度
- min 数据最大值
- max 数据最小值
- extent 经纬度范围
```javascript
source(values, {
parser: {
type: 'raster',
width: n,
height: m,
min: 0,
max: 8000,
extent: [73.482190241, 3.82501784112, 135.106618732, 57.6300459963],
},
});
```
[Image 数据格式解析](./image)
### transforms
目前支持三种数据处理方法 mapgridhexagon transform 配置项
目前支持两种热力图使用的数据处理方法 gridhexagon transform 配置项
- type 数据处理类型
- tansform cfg  数据处理配置项
#### map
数据处理,支持自定义 callback 函数
- callback:function 回调函数
```javascript
layer.source(data, {
transforms: [
{
type: 'map',
callback: function(item) {
const [x, y] = item.coordinates;
item.lat = item.lat * 1;
item.lng = item.lng * 1;
item.v = item.v * 1;
item.coordinates = [x * 1, y * 1];
return item;
},
},
],
});
```
#### grid
生成方格网布局,根据数据字段统计,主要在网格热力图中使用

View File

@ -15,8 +15,11 @@ Current version: ![L7 2.0版本号](https://badgen.net/npm/v/@antv/l7/beta)
Include the L7 JS JavaScript <head> of your HTML file.
:warning: 如果需要引用第三方地图API请确保在先引入第三方API然后引入L7
```html
<head>
<! --引入第三方地图JSAPI-->
<script src='https://gw.alipayobjects.com/os/antv/pkg/_antv.l7-2.0.0-beta.19/dist/l7.js'>
</script>
</head>
@ -41,6 +44,7 @@ npm install --save @antv/l7-maps;
```
### 初始化地图
#### 使用 高德 底图
@ -80,3 +84,76 @@ const scene = new Scene({
```
### React中使用
React 组件待开发期待和大家共建l7-react 目前可以暂时以 Submodule 方式使用
```
import { Scene, PolygonLayer } from '@antv/l7';
import { AMap } from '@antv/l7-maps';
import * as React from 'react';
export default class AMapExample extends React.Component {
private scene: Scene;
public componentWillUnmount() {
this.scene.destroy();
}
public async componentDidMount() {
const response = await fetch(
'https://gw.alipayobjects.com/os/basement_prod/d2e0e930-fd44-4fca-8872-c1037b0fee7b.json',
);
const scene = new Scene({
id: 'map',
map: new AMap({
center: [110.19382669582967, 50.258134],
pitch: 0,
style: 'dark',
zoom: 3,
token: 'pg.xxx', // 高德或者 Mapbox 的 token
}),
});
const layer = new PolygonLayer({});
layer
.source(await response.json())
.size('name', [0, 10000, 50000, 30000, 100000])
.color('name', [
'#2E8AE6',
'#69D1AB',
'#DAF291',
'#FFD591',
'#FF7A45',
'#CF1D49',
])
.shape('fill')
.style({
opacity: 0.8,
});
scene.addLayer(layer);
this.scene = scene;
}
public render() {
return (
<div
id="map"
style={{
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: 0,
}}
/>
);
}
}
```
⚠️组件 Unmount 时需要通过 scene.destroy() 手动销毁场景。
更多React使用 [示例查看](https://github.com/antvis/L7/tree/master/stories)
### Vue 欢迎补充

View File

@ -44,14 +44,17 @@ const scene = new Scene({
zoom: 3.802
})
});
Promise.all([
addChart();
scene.render();
function addChart() {
Promise.all([
fetch(
'https://gw.alipayobjects.com/os/basement_prod/5b772136-a1f4-4fc5-9a80-9f9974b4b182.json'
).then(d => d.json()),
fetch(
'https://gw.alipayobjects.com/os/basement_prod/f3c467a4-9ae0-4f08-bb5f-11f9c869b2cb.json'
).then(d => d.json())
]).then(function onLoad([ center, population ]) {
]).then(function onLoad([ center, population ]) {
const popobj = {};
population.forEach(element => {
popobj[element.Code] =
@ -107,4 +110,5 @@ Promise.all([
});
scene.addMarker(marker);
});
});
});
}

View File

@ -11,7 +11,7 @@ export default class MarkerService implements IMarkerService {
private markers: IMarker[] = [];
private unAddMarkers: IMarker[] = [];
public addMarker(marker: IMarker): void {
if (!this.mapsService.map && this.mapsService.getMarkerContainer()) {
if (this.mapsService.map && this.mapsService.getMarkerContainer()) {
this.markers.push(marker);
marker.addTo(this.scene);
} else {

View File

@ -106,7 +106,6 @@ export interface ILayer {
*/
addPlugin(plugin: ILayerPlugin): ILayer;
getSource(): ISource;
isSourceNeedUpdate(): boolean;
setSource(source: ISource): void;
setEncodedData(encodedData: IEncodeFeature[]): void;
getEncodedData(): IEncodeFeature[];

View File

@ -61,6 +61,7 @@ export interface ISource {
data: IParserData;
cluster: boolean;
clusterOptions: Partial<IClusterOptions>;
updateClusterData(zoom: number): void;
}
export interface IRasterCfg {
extent: [number, number, number, number];

View File

@ -371,30 +371,6 @@ export default class BaseLayer<ChildLayerStyleOptions = {}> extends EventEmitter
this.buildModels();
return this;
}
public isSourceNeedUpdate() {
const cluster = this.layerSource.cluster;
if (cluster) {
const { zoom = 0, bbox = [0, 0, 0, 0] } = this.layerSource.clusterOptions;
const newZoom = this.mapService.getZoom();
const bounds = this.mapService.getBounds();
const newBbox: [number, number, number, number] = [
bounds[0][0],
bounds[0][1],
bounds[1][0],
bounds[1][1],
];
// ||
// bbox[0] !== newBbox[0] ||
// bbox[2] !== newBbox[2]
if (Math.abs(zoom - newZoom) > 1) {
this.layerSource.updateClusterData(Math.floor(newZoom), newBbox);
return true;
}
}
return false;
}
public style(options: object & Partial<ILayerConfig>): ILayer {
const { passes, ...rest } = options;
@ -537,12 +513,7 @@ export default class BaseLayer<ChildLayerStyleOptions = {}> extends EventEmitter
const bounds = this.mapService.getBounds();
const zoom = this.mapService.getZoom();
if (this.layerSource.cluster) {
this.layerSource.updateClusterData(zoom, [
bounds[0][0],
bounds[0][1],
bounds[1][0],
bounds[1][1],
]);
this.layerSource.updateClusterData(zoom);
}
}
public getSource() {

View File

@ -1,10 +1,12 @@
import { ILayer, ILayerPlugin } from '@antv/l7-core';
import { ILayer, ILayerPlugin, IMapService, TYPES } from '@antv/l7-core';
import Source from '@antv/l7-source';
import { injectable } from 'inversify';
@injectable()
export default class DataSourcePlugin implements ILayerPlugin {
protected mapService: IMapService;
public apply(layer: ILayer) {
this.mapService = layer.getContainer().get<IMapService>(TYPES.IMapService);
layer.hooks.init.tap('DataSourcePlugin', () => {
const { data, options } = layer.sourceOption;
layer.setSource(new Source(data, options));
@ -12,7 +14,12 @@ export default class DataSourcePlugin implements ILayerPlugin {
// 检测数据是不否需要更新
layer.hooks.beforeRenderData.tap('DataSourcePlugin', (flag) => {
if (layer.isSourceNeedUpdate()) {
const source = layer.getSource();
const cluster = source.cluster;
const { zoom = 0, maxZoom = 16 } = source.clusterOptions;
const newZoom = this.mapService.getZoom();
if (cluster && Math.abs(zoom - newZoom) > 1 && maxZoom > zoom) {
source.updateClusterData(Math.floor(newZoom) + 1);
return true;
}
return false;

View File

@ -20,6 +20,6 @@ describe('source constructor', () => {
field: 'mag',
},
});
source.updateClusterData(2, [10, 0, 130, 75]);
source.updateClusterData(2);
});
});

View File

@ -1,9 +1,12 @@
import {
IClusterOptions,
IMapService,
IParserCfg,
IParserData,
ISourceCFG,
ITransform,
lazyInject,
TYPES,
} from '@antv/l7-core';
import { extent } from '@antv/l7-utils';
import {
@ -14,13 +17,13 @@ import {
Properties,
} from '@turf/helpers';
import { EventEmitter } from 'eventemitter3';
import { Container } from 'inversify';
import { cloneDeep, isFunction, isString } from 'lodash';
import Supercluster from 'supercluster';
import { SyncHook } from 'tapable';
import { getParser, getTransform } from './';
import { statMap } from './utils/statistics';
import { getColumn } from './utils/util';
export default class Source extends EventEmitter {
public data: IParserData;
@ -82,13 +85,9 @@ export default class Source extends EventEmitter {
this.init();
}
public updateClusterData(
zoom: number,
bbox: [number, number, number, number],
): void {
public updateClusterData(zoom: number): void {
const { method = 'sum', field } = this.clusterOptions;
let data = this.clusterIndex.getClusters(bbox, zoom);
this.clusterOptions.bbox = bbox;
let data = this.clusterIndex.getClusters(this.extent, zoom);
this.clusterOptions.zoom = zoom;
data.forEach((p) => {
if (!p.id) {

View File

@ -1,5 +1,6 @@
import { storiesOf } from '@storybook/react';
import * as React from 'react';
import Chart from './components/chart';
import Marker from './components/Marker';
import Popup from './components/Popup';
import Scale from './components/Scale';
@ -9,4 +10,5 @@ storiesOf('UI 组件', module)
.add('Zoom', () => <Zoom />)
.add('Scale', () => <Scale />)
.add('Marker', () => <Marker />)
.add('Chart', () => <Chart />)
.add('Popup', () => <Popup />);

View File

@ -0,0 +1,108 @@
// @ts-ignore
import * as G2 from '@antv/g2';
import { Marker, Scene } from '@antv/l7';
import { GaodeMap, Mapbox } from '@antv/l7-maps';
import * as React from 'react';
export default class ChartComponent extends React.Component {
private scene: Scene;
public componentWillUnmount() {
this.scene.destroy();
}
public async componentDidMount() {
const scene = new Scene({
id: 'map',
map: new GaodeMap({
pitch: 0,
style: 'dark',
center: [52.21496184144132, 24.121126851768906],
zoom: 3.802,
}),
});
addChart();
scene.render();
function addChart() {
Promise.all([
fetch(
'https://gw.alipayobjects.com/os/basement_prod/5b772136-a1f4-4fc5-9a80-9f9974b4b182.json',
).then((d) => d.json()),
fetch(
'https://gw.alipayobjects.com/os/basement_prod/f3c467a4-9ae0-4f08-bb5f-11f9c869b2cb.json',
).then((d) => d.json()),
]).then(function onLoad([center, population]) {
const popobj: { [key: string]: any } = {};
population.forEach((element: any) => {
popobj[element.Code] =
element['Population, female (% of total) (% of total)'];
});
// 数据绑定
center.features = center.features.map((fe: any) => {
fe.properties.female = popobj[fe.properties.id] * 1 || 0;
return fe;
});
center.features.forEach((point: any) => {
const el = document.createElement('div');
const coord = point.geometry.coordinates;
const v = (point.properties.female * 1) as number;
if (v < 1 || (v > 46 && v < 54)) {
return;
}
const size = 60;
const data = [
{
type: '男性',
value: 100.0 - Number(v.toFixed(2)),
},
{
type: '女性',
value: v.toFixed(2),
},
];
const chart = new G2.Chart({
container: el,
width: size,
height: size,
// render: 'svg',
padding: 0,
});
chart.source(data);
chart.legend(false);
chart.tooltip(false);
chart.coord('theta', {
radius: 0.9,
innerRadius: 0.6,
});
chart
.intervalStack()
.position('value')
.color('type', ['#5CCEA1', '#5B8FF9'])
.opacity(1);
chart.render();
const marker = new Marker({ element: el }).setLnglat({
lng: coord[0],
lat: coord[1],
});
scene.addMarker(marker);
});
});
}
}
public render() {
return (
<div
id="map"
style={{
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: 0,
}}
/>
);
}
}

View File

@ -36,9 +36,6 @@ export default class Point3D extends React.Component {
.scale('point_count', {
type: 'quantile',
})
.filter('point_count', (point_count: number) => {
return point_count > 1;
})
.size('point_count', [5, 10, 15, 20, 25])
.color('red')
.style({
@ -46,7 +43,6 @@ export default class Point3D extends React.Component {
strokeWidth: 1,
});
scene.addLayer(pointLayer);
console.log(pointLayer);
}
public render() {