jhgrrewq 前端小站

置我于死地者 必将赐我以后生


  • 首页

  • 标签

  • 归档

  • 搜索

未命名

发表于 2021-12-29

一、介绍

1. 地理应用分层

2. cci-map 介绍

cci-map 是基于 @antv/l7 二次开发的地图渲染引擎,使用 WebGL 渲染技术可支持海量数据点加载,功能包括:地图底图切换、地图控制组件、可视化图层等。(L7 是蚂蚁金服基于 WebGL 的开源大规模地理空间数据可视分析开发框: 2D,3D 一体化的海量数据高性能渲染; 支持 CSV,JSON,geojson 等数据格式接入,可按需求自定义数据格式; 多地图底图支持,支持离线内网部署,见文档https://l7.antv.vision/zh/docs/api/map)

3. 安装

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 1. 使用cci 私有仓库

npm config set registry http://ccinpm.citycloud.com.cn

# 推荐使用 nrm 管理 registry

npm install nrm -g

nrm add cci http://ccinpm.citycloud.com.cn

nrm ls #查看registry列表

nrm use cci #使用 cci 仓库

# 2. 安装依赖
npm install cci-map

# 推荐使用 yarn 安装依赖, 必要是可以删除 yarn.lock 文件重新安装
# 安装 yarn
npm install yarn -g

yarn add cci-map

# 使用绘制工具需要安装依赖
npm install cci-lib-l7-draw
yarn add cci-lib-l7-draw

4. 快速上手

cci-map 兼容 @antv/l7用法,暴露 CMap 类作为场景构造类(@antv/l7对应 Scene 类),并对 @antv/l7 升级和扩展了部分功能。

4.1 cci-map 使用 ES module 引入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import { CMap, GaodeMap } from 'cci-map'

const cmap = new CMap({
id: 'map',
map: new GaodeMap({
zoom: 4, // 初始化地图层级
pitch: 50,
rotation: 0,
maxZoom: 30,
minZoom: 0,
center: [105.404, 28.915], // 初始化地图中心点
token: '48463d4ad0a6bd804a336d9ed317717e'
})
})
cmap.on('loaded', () => {})

4.2 cci-map 使用 script 脚本引入

如需引用第三方地图API,请确保在先引入第三方API后再引入 cci-map ( 目前 umd 版本在 cci-map 库 dist 目录下 index.umd.js 文件,后续会开放 cdn )

所有构造类和方法在命名空间 CciMap 下

1
<script src = 'cci-map/dist/index.umd.js'></script>
4.2.1 使用高德地图
  • 注册账号并申请Key
  1. 首先,注册开发者账号,成为高德开放平台开发者
  2. 登陆之后,在进入「应用管理」 页面「创建新应用」
  3. 为应用添加 Key,「服务平台」一项请选择「 Web 端 ( JSAPI ) 」
  • 引入 cci-map 脚本

2.0版本在cci-map内部动态引入了高德地图JS API,因此不再需要单独引入高德JS API,只需设置 type 为 amap 并且传入token

1
<script src='cci-map/dist/index.umd.js'></script>
  • 完整代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>创建地图场景</title>
<style>
html,body{overflow:hidden;margin:0;}
#map { position:absolute; top:0; bottom:0; width:100%; }
</style>
</head>
<body>
<div id="map"></div>
<!-- 1. 需要先引入第三方地图 api -->
<!-- 2. 引入 cci-map 脚本 -->
<script src="cci-map/dist/index.umd.js"></script>
<script>
// 3. 初始化 cci-map 场景构造类
const scene = new CciMap.CMap({
id: 'map',
map: new CciMap.GaodeMap({
style: 'dark', // 样式URL
center: [120.19382669582967, 30.258134],
pitch: 0,
zoom: 12,
token: '高德地图token',
}),
});

// 4. 添加可视化图层(获取数据后初始化图层)
fetch('https://gw.alipayobjects.com/os/rmsportal/oVTMqfzuuRFKiDwhPSFL.json')
.then(res => res.json())
.then(data => {
const pointLayer = new CciMap.PointLayer({})
.source(data.list, {
parser: {
type: 'json',
x: 'j',
y: 'w'
}
})
.shape('cylinder')
.size('t', function(level) {
return [ 1, 2, level * 2 + 20 ];
})
.color('t', [
'#094D4A',
'#146968',
'#1D7F7E',
'#289899',
'#34B6B7',
'#4AC5AF',
'#5FD3A6',
'#7BE39E',
'#A1EDB8',
'#CEF8D6'
])
.style({
opacity: 1.0
});
scene.addLayer(pointLayer);
});

</script>
</body>
</html>
4.2.2 使用 mapbox
  • 注册MapBox token

注册地址 Mapbox Access Tokens

  • 引入mapbox.gl JS 和 css

使用mapbox 需要单独引入 mapbox

1
2
<link href='https://api.tiles.mapbox.com/mapbox-gl-js/v1.5.0/mapbox-gl.css' rel='stylesheet' />
<script src='https://api.tiles.mapbox.com/mapbox-gl-js/v1.5.0/mapbox-gl.js'></script>
  • 引入 cci-map 脚本
1
<script src='cci-map/dist/index.umd.js'></script>
  • 完整代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>创建地图场景</title>
<style> ::-webkit-scrollbar{display:none;}html,body{overflow:hidden;margin:0;}
#map { position:absolute; top:0; bottom:0; width:100%; }
</style>
</head>
<body>
<div id="map"></div>
<!-- 1. 需要先引入第三方地图 api -->
<link href='https://api.tiles.mapbox.com/mapbox-gl-js/v1.5.0/mapbox-gl.css' rel='stylesheet' />
<script src='https://api.tiles.mapbox.com/mapbox-gl-js/v1.5.0/mapbox-gl.js'></script>
<!-- 2. 引入 cci-map 脚本 -->
<script src="cci-map/dist/index.umd.js"></script>
<script>
// 3. 初始化 cci-map 场景构造类
const scene = new CciMap.CMap({
id: 'map',
map: new CciMap.Mapbox({
style: 'dark', // 样式URL
center: [120.19382669582967, 30.258134],
pitch: 0,
zoom: 12,
token: 'mapbox token',
}),
});
// 4. 添加可视化图层(获取数据后初始化图层)
fetch('https://gw.alipayobjects.com/os/rmsportal/oVTMqfzuuRFKiDwhPSFL.json')
.then(res => res.json())
.then(data => {
const pointLayer = new CciMap.PointLayer({})
.source(data.list, {
parser: {
type: 'json',
x: 'j',
y: 'w'
}
})
.shape('cylinder')
.size('t', function(level) {
return [ 1, 2, level * 2 + 20 ];
})
.color('t', [
'#094D4A',
'#146968',
'#1D7F7E',
'#289899',
'#34B6B7',
'#4AC5AF',
'#5FD3A6',
'#7BE39E',
'#A1EDB8',
'#CEF8D6'
])
.style({
opacity: 1.0
});
scene.addLayer(pointLayer);
});

</script>
</body>
</html>
4.2.3 使用 maptalks
  • 引入maptalks JS 和 css

使用mapbox 需要单独引入 mapbox

1
2
<link href="https://cdn.bootcdn.net/ajax/libs/maptalks/0.49.0/maptalks.min.css" rel="stylesheet">
<script src="https://cdn.bootcdn.net/ajax/libs/maptalks/0.49.0/maptalks.min.js"></script>
  • 引入 cci-map 脚本
1
<script src='cci-map/dist/index.umd.js'></script>
  • 完整代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>创建地图场景</title>
<style> ::-webkit-scrollbar{display:none;}html,body{overflow:hidden;margin:0;}
#map { position:absolute; top:0; bottom:0; width:100%; }
</style>
</head>
<body>
<div id="map"></div>
<!-- 1. 需要先引入第三方地图 api -->
<link href="https://cdn.bootcdn.net/ajax/libs/maptalks/0.49.0/maptalks.min.css" rel="stylesheet">
<script src="https://cdn.bootcdn.net/ajax/libs/maptalks/0.49.0/maptalks.min.js"></script>
<!-- 2. 引入 cci-map 脚本 -->
<script src="cci-map/dist/index.umd.js"></script>
<script>
// 3. 初始化 cci-map 场景构造类
const scene = new CciMap.CMap({
id: 'map',
map: new CciMap.MapTalks({
style: 'dark', // 样式URL
center: [120.19382669582967, 30.258134],
pitch: 0,
zoom: 12
}),
});
// 4. 添加可视化图层(获取数据后初始化图层)
fetch('https://gw.alipayobjects.com/os/rmsportal/oVTMqfzuuRFKiDwhPSFL.json')
.then(res => res.json())
.then(data => {
const pointLayer = new CciMap.PointLayer({})
.source(data.list, {
parser: {
type: 'json',
x: 'j',
y: 'w'
}
})
.shape('cylinder')
.size('t', function(level) {
return [ 1, 2, level * 2 + 20 ];
})
.color('t', [
'#094D4A',
'#146968',
'#1D7F7E',
'#289899',
'#34B6B7',
'#4AC5AF',
'#5FD3A6',
'#7BE39E',
'#A1EDB8',
'#CEF8D6'
])
.style({
opacity: 1.0
});
scene.addLayer(pointLayer);
});

</script>
</body>
</html>

4.3 cci-lib-draw 快速上手

4.3.1 使用
  • es6 module
1
import { DrawControl } from 'cci-lib-l7-draw'
  • 使用 script 脚本引入

    如需引用第三方地图API,请确保在先引入第三方API后再引入 cci-map, 再引入 cci-lib-l7-draw ( 目前 umd 版本在 cci-lib-l7-draw 库 dist 目录下 index.umd.js 文件,后续会开放 cdn )

    所有构造类和方法在命名空间 CciMapDraw 下

    1
    <script src='cci-lib-l7-draw/dist/index.umd.js'></script>
4.3.2 实例化
1
2
const control = new DrawControl(scene, option)
scene.addControl(control)

二、 自定义投影坐标系

l7 只接入高德和 mapbox 地图引擎,而 cci-map 还接入了 maptalks。对于自定义天地图投影需求,可以指定底图为 maptalks,结合 proj4 实现。

  • Proj4js 是地理坐标参考系的转换javascript库, 它同时支持不同大地基准面的转换
  • 对于不同坐标系,默认 resolution 和 fullExtent 参考 maptalks 源码 DefaultSpatialReference 部分(定义了 EPSG3857,EPSG4490,EPSG4326,百度等)

2.1 maptalks 自定义投影 EPSG4490 天地图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
<script>
import { CMap, MapTalks, WMSLayer } from 'cci-map'
// import { CMap, MapTalks } from 'cci-map'
// import { WMSLayer } from 'cci-layers'
import * as maptalks from 'maptalks'
import proj4 from 'proj4'

export default {
beforeDestroy() {
this.scene.destroy()
},
mounted() {
// 对比 leaflet proj4 设置
/* const CRS_4490 = new L.Proj.CRS(
"EPSG:4490",
"+proj=longlat +ellps=GRS80 +no_defs",
{
resolutions: [
0.00549933137239034, // Level 0
0.00274966568619517, // Level 1
0.00137483284309758, // Level 2
0.000687416421548792, // Level 3
0.000343708210774396, // Level 4
0.000171854105387198,
8.5927052693599e-5,
4.29635263467995e-5,
2.14817631733998e-5,
1.07408815866999e-5,
5.37044079334994e-6,
2.68522039667497e-6,
1.34261019833748e-6
],
origin: [118.122911693886, 31.2869311022836]
}
) */

// 自定义投影坐标系天地图
/* proj4.defs('EPSG4490', "+proj=longlat +ellps=GRS80 +no_defs")
const projection = proj4('EPSG4490') */
const proj4490 = "+proj=longlat +ellps=GRS80 +no_defs"
const projection = proj4('WGS84', proj4490)
const spatialReference = {
projection: {
code: 'EPSG4490',
project: function(c) {
const pc = projection.forward(c.toArray())
return new maptalks.Coordinate(pc)
},
unproject: function(pc) {
const c = projection.inverse(pc.toArray())
return new maptalks.Coordinate(c)
}
},
resolutions: [
0.00549933137239034, // Level 0
0.00274966568619517, // Level 1
0.00137483284309758, // Level 2
0.000687416421548792, // Level 3
0.000343708210774396, // Level 4
0.000171854105387198,
8.5927052693599e-5,
4.29635263467995e-5,
2.14817631733998e-5,
1.07408815866999e-5,
5.37044079334994e-6,
2.68522039667497e-6,
1.34261019833748e-6
],
'fullExtent': {
'top': 90,
'left': -180,
'bottom': -90,
'right': 180
}
}

const mapInstance = new maptalks.Map('map', {
center: [119.822911693886, 30.0869311022836],
zoom: 0,
spatialReference, // 地图需要设置 spatialReference
baseLayer: new maptalks.TileLayer('base', {
urlTemplate: 'http://183.134.200.102:8080/DD1223/E36CCEA93443D1495DB9B9F2B2FFE348CB1A367D75176F815040AB19E54CDCA5DAAA25813AF965E2ABD0CC2463DD1223/PBS/rest/services/hzsyvector/Mapserver/tile/{z}/{y}/{x}',
spatialReference,
tileSystem: [1, -1, 118.122911693886, 31.2869311022836] // 要设置图层 spatialReference 和 tileSystem
})
})
const scene = new CMap({
id: 'map',
map: new MapTalks({
mapInstance
})
})
this.scene = scene

scene.on('loaded', () => {
const layer = new WMSLayer({
tileSize: 256
})
layer.source(
{
url: '/geoserver/wms',
params: {
layers: 'multi_polygon',
version: '1.1.1',
format: 'image/png',
transparent: true
}
},
{
parser: {
type: 'wms',
crs: 'EPSG:4326'
}
}
)
scene.addLayer(layer)
})
}
}
</script>
<template>
<div
id="map"
style="
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
"
/>
</template>

2.2 proj4js来实现默认的EPSG:3857空间

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
<template>
<div style="width:100%;height:100%">
<div id="map"/>
</div>
</template>
<script>
import { CMap, MapTalks } from 'cci-map'
import * as maptalks from 'maptalks'
import proj4 from 'proj4'
export default {
methods: {
},
mounted() {
// EPSG:3857's proj definition
var proj3857 = '+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs';
const proj = proj4('WGS84', proj3857);

// define a custom projection object
var projection = {
code : 'proj4-merc', // code of the projection

project : function (c) { // from wgs84 to EPSG3857
var pc = proj.forward(c.toArray());
return new maptalks.Coordinate(pc);
},

unproject : function (pc) { // from EPSG3857 to wgs84
var c = proj.inverse(pc.toArray());
return new maptalks.Coordinate(c);
}
};

var mapInstance = new maptalks.Map('map', {
center: [-0.113049, 51.498568],
zoom: 13,
// spatial reference definition
spatialReference : {
projection : projection, // geo projection, defined by proj4js
resolutions : [ // map's zoom levels and resolutions
156543.03392804097,
78271.51696402048,
9135.75848201024,
19567.87924100512,
9783.93962050256,
4891.96981025128,
2445.98490512564,
1222.99245256282,
611.49622628141,
305.748113140705,
152.8740565703525,
76.43702828517625,
38.21851414258813,
19.109257071294063,
9.554628535647032,
4.777314267823516,
2.388657133911758,
1.194328566955879,
0.5971642834779395,
0.29858214173896974
],
fullExtent : { // map's full extent
'top': 6378137 * Math.PI,
'left': -6378137 * Math.PI,
'bottom': -6378137 * Math.PI,
'right': 6378137 * Math.PI
}
},
baseLayer : new maptalks.TileLayer('base',{
urlTemplate: 'https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}.png',
subdomains: ['a','b','c','d'],
attribution: '&copy; <a href="http://osm.org">OpenStreetMap</a> contributors, &copy; <a href="https://carto.com/">CARTO</a>'
})
});

const scene = new CMap({
id: 'map',
map: new MapTalks({
mapInstance
})
})

scene.on('loaded', () => {
console.log('test::::::;');
})
}
}
</script>
<style>
::-webkit-scrollbar {
display: none;
}
#map {
position: absolute;
top: 0;
bottom: 0;
width: 100%;
}
#map .l7-scene {
position: absolute !important;
}
</style>

三、 cci-map gis工具

1. BD09,GCJ02 和 WGS84 坐标系间的转换工具

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
import { CoordsTransform  } from 'cci-map'
// import { CoordsTransform } from 'cci-utils'
const lng = 120
const lat = 30
/**
* 百度坐标系 (BD-09) 与 火星坐标系 (GCJ-02)的转换
* 即 百度 转 谷歌、高德
* @param lng
* @param lat
* @returns {*[]}
*/
CoordsTransform.bd09togcj02(lng, lat)
/**
* 火星坐标系 (GCJ-02) 与百度坐标系 (BD-09) 的转换
* 即谷歌、高德 转 百度
* @param lng
* @param lat
* @returns {*[]}
*/
CoordsTransform.gcj02tobd09(lng, lat)
/**
* WGS84转GCj02
* @param lng
* @param lat
* @returns {*[]}
*/
CoordsTransform.wgs84togcj02(lng, lat)
/**
* GCJ02 转换为 WGS84
* @param lng
* @param lat
* @returns {*[]}
*/
CoordsTransform.gcj02towgs84(lng, lat)
/**
* 百度坐标系 (BD-09) 与 WGS84的转换
* 即 百度 转 谷歌
* @param lng
* @param lat
* @returns {*[]}
**/
CoordsTransform.bd09towgs84(lng, lat)
/**
* WGS84 与 百度坐标系 (BD-09)的转换
* 即 谷歌 转 百度
* @param lng
* @param lat
* @returns {*[]}
**/
CoordsTransform.wgs84tobd09(lng, lat)

2. 数据抽稀(针对大数据量边界数据)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
import { simplifyGeoJSON } from 'cci-map'
// import { simplifyGeoJSON } from 'cci-utils'
/**
* @param geojson 数据
* @param tolerance 误差(可不传)
* @returns {}
**/
const geojson = {
type: 'FeatureCollection',
features: [
{
type: 'Feature',
properties: {},
geometry: {
type: 'Polygon',
coordinates: [
[
[
116.30470275878906,
39.88352811449648
],
[
116.32083892822264,
39.89380183825623
],
[
116.31637573242188,
39.89617247892832
],
[
116.30556106567381,
39.89577737784395
],
[
116.30281448364258,
39.89709437260048
],
[
116.28822326660156,
39.90657598772839
],
[
116.27809524536131,
39.901571965464
],
[
116.27843856811523,
39.880103197763546
],
[
116.28822326660156,
39.87457027859936
],
[
116.29131317138673,
39.85928656392012
],
[
116.29371643066405,
39.852302354195864
],
[
116.3129425048828,
39.853620184014325
],
[
116.3393783569336,
39.85414730885731
],
[
116.3448715209961,
39.85796884289976
],
[
116.3448715209961,
39.87233063679467
],
[
116.3422966003418,
39.885240508711654
],
[
116.32564544677734,
39.889060310919994
],
[
116.31465911865234,
39.88813830918363
],
[
116.30470275878906,
39.88352811449648
]
]
]
}
}
]
}
var simplified = simplifyGeoJSON(geojson, tolerance)

3. 浏览器辅助 ( cci-map 的 Browser 类 )

属性名 类型 说明
ua String 当前浏览器userAgent
mobile Boolean 是否移动设备
plat String 平台类型,如:’windows’、’mac’、’ios’、’android’、’other’
windows Boolean 是否windows设备
ios Boolean 是否iOS设备
iPad Boolean 是否iPad
iPhone Boolean 是否iPhone
android Boolean 是否安卓设备
android23 Boolean 是否安卓4以下系统
chrome Boolean 是否Chrome浏览器
firefox Boolean 是否火狐浏览器
safari Boolean 是否Safari浏览器
wechat Boolean 是否微信
uc Boolean 是否UC浏览器
qq Boolean 是否QQ或者QQ浏览器
ie Boolean 是否IE
ie6 Boolean 是否IE6
ie7 Boolean 是否IE7
ie8 Boolean 是否IE8
ie9 Boolean 是否IE9
ie10 Boolean 是否IE10
ie11 Boolean 是否IE11
ielt9 Boolean 是否IE9以下
edge Boolean 是否Edge浏览器
isLocalStorage Boolean 是否支持LocaStorage
isGeolocation Boolean 是否支持Geolocation
mobileWebkit Boolean 是否Webkit移动浏览器
mobileWebkit3d Boolean 是否支持Css3D的Webkit移动端浏览器
retina Boolean 是否高清屏幕,devicePixelRatio>1
touch Boolean 是否触屏
msPointer Boolean 是否msPointer设备
pointer Boolean 是否pointer设备
webkit Boolean 是否webkit浏览器
webkit3d Boolean 是否支持Css3D的Webkit浏览器
gecko3d Boolean 是否支持Css3D的gecko浏览器
ie3d Boolean 是否支持Css3D的ie浏览器
any3d Boolean 是否支持Css3D的浏览器
opera3d Boolean 是否支持Css3D的opera浏览器
isCanvas Boolean 是否支持canvas
isSvg Boolean 是否支持svg
isVML Boolean 是否支持vml
isWorker Boolean 是否支持WebWorker
isWebsocket Boolean 是否支持WebSocket
isWebGL function 判断是否支持webgl
1
2
3
import { Browser } from 'cci-map'
// import { Browser } from 'cci-utils'
console.log('browser.chrome', Browser.chrome)

四、cci-map 绘制工具

cci-lib-l7-draw 二次开发的地图绘制组件,支持点、marker、线、面, 圆、矩形、的绘制编辑,支持矩形、多边形和圆形框选缩放,支持距离和面积测量

1. DrawControl

1.1 使用

1
import { DrawControl } from 'cci-lib-l7-draw';

1.2 参数

1
const control = new DrawControl(scene, option);

1.2.1 scene

scene 对象, 绘制控件需要传入 scene 实例

1.2.2 options

control 配置项

name Type Default Description
position bottomright、topright、 bottomleft’ topleft topright 组件位置
layout horizontal、 vertical vertical 组件布局
controls controlOptions 设置 UI 组件添加哪些绘制工具
style 地图绘制样式

1.2.3 UI 组件配置项

  • point boolean | drawOption 绘制点工具配置
  • marker boolean | drawOption 绘制 marker 工具配置
  • line boolean | drawOption 绘制线工具配置
  • polygon boolean | drawOption 绘制面工具配置
  • circle boolean | drawOption 绘制圆工具配置
  • rect boolean | drawOption 绘制矩形工具配置
  • delete boolean | drawOption 添加删除工具
  • calculateLine boolean 添加测量距离工具
  • calculateArea boolean 添加测量面积工具

1.2.4 默认配置

1
2
3
4
5
6
7
8
9
10
11
{
point: true,
marker: true,
line: true,
polygon: true,
rect: true,
circle: true,
delete: true,
calculateLine: true,
calculateArea: true,
}

1.2.5 示例

1
2
3
4
5
6
7
8
9
10
11
12
13
{
point: false,
marker: false,
line: {
editEnable: false,
},
polygon: true,
rect: true,
circle: true,
delete: false,
calculateLine: true,
calculateArea: true,
}

1.3 添加到地图

1
scene.addControl(control);

1.4 从地图中移除

1
scene.removeControl(control);

1.5 方法

1
getDraw()

参数: type 绘制实例 point|marker|line|polygon|rect|circle

1
const pointDraw = drawcontrol.get('point');

getAllData() 获取每个 Draw 实例绘制的结果数据

返回数据格式如下

1
2
3
4
{
point: []
line: [];
}

1.6 事件

drawControl 的事件类型和每个 Draw 的事件一致,如果在 drawControl 监听事件会为每个 draw 增加事件监听

  • drawType: Draw 类型
  • feature: 对应的数据

draw.create 绘制完成时触发该事件

draw.delete 图形删除时触发该事件

draw.update 图形更新时触发该事件,图形的平移,顶点的编辑

draw.selectionchange 图形选中时触发该事件

1
drawControl.on('draw.delete', e => {});

1.7 style

drawControl 的 style 和每个 Draw 的 style 一致

2. Draw Type

可以不依赖 Draw UI 组件,独立的使用每一个 Draw

2.1 DrawMarker (新增功能)

绘制 marker

1
2
3
import { DrawMarker } from 'cci-lib-l7-draw';
const drawMarker = new DrawCircle(scene);
drawMarker.enable();

参数使用 data 选项时候, geojson 的 propertise 需要添加对应类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
new DrawMarker(scene, {
data: {
type: 'FeatureCollection',
features: [
{
type: 'Feature',
properties: {
markerOption: {} // 传入 Marker 构造函数配置项进行自定义设置;传空对象则渲染默认 marker
},
geometry: {
type: 'Point',
coordinates: markerCoordinates
}
}
]
}
});

2.2 DrawCircle

绘制圆形

1
2
3
import { DrawCircle } from 'cci-lib-l7-draw';
const drawCircle = new DrawCircle(scene);
drawCircle.enable();

参数使用 data 选项初始化渲染

1
2
3
4
5
6
7
8
9
const draw = new DrawCircle(scene, {
data: {
type: 'circle',
features: [
[127.61718749999999, 54.97761367069628], // 中心点
[117.42187500000001, 23.241346102386135], // 圆上点
],
}
})

或者调用 resetData 方法

1
2
3
4
5
6
7
8
const draw = new DrawCircle(scene)
draw.resetData({
type: 'circle',
features: [
[127.61718749999999, 54.97761367069628], // 中心点
[117.42187500000001, 23.241346102386135], // 圆上点
],
})

2.3 DrawRect

绘制四边形

1
2
3
import { DrawRect } from 'cci-lib-l7-draw';
const drawRect = new DrawRect(scene);
drawRect.enable();

参数使用 data 选项渲染初始化数据

1
2
3
4
5
6
7
8
9
const draw = new DrawRect(scene, {
data: {
type: 'rect',
features: [
[127.61718749999999, 54.97761367069628], // 对角线两点
[117.42187500000001, 23.241346102386135],
],
}
})

或者调用 resetData 方法

1
2
3
4
5
6
7
8
const draw = new DrawRect(scene)
draw.resetData({
type: 'rect',
features: [
[127.61718749999999, 54.97761367069628], // 对角线两点
[117.42187500000001, 23.241346102386135],
],
})

2.4 DrawLine

绘制路径

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import { DrawLine } from 'cci-lib-l7-draw';
const drawLine = new DrawLine(scene);
drawLine.enable();
new DrawLine(scene, {
data: {
type: 'FeatureCollection',
features: [
{
type: 'Feature',
properties: {},
geometry: {
type: 'LineString',
coordinates: lineCoordinates
}
}
]
}
});

2.5 DrawPoint

绘制点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import { DrawPoint } from 'cci-lib-l7-draw';
const drawPoint = new DrawPoint(scene);
drawPoint.enable();
new DrawPoint(scene, {
data: {
type: 'FeatureCollection',
features: [
{
type: 'Feature',
properties: {},
geometry: {
type: 'Point',
coordinates: pointCoordinates
}
}
]
}
});

2.6 DrawPolygon

绘制多边形

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import { DrawPolygon } from 'cci-lib-l7-draw';
const drawPoint = new DrawPolygon(scene);
drawPoint.enable();
new DrawPolygon(scene, {
data: {
type: 'FeatureCollection',
features: [
{
type: 'Feature',
properties: {},
geometry: {
type: 'Polygon',
coordinates: polygonCoordinates
}
}
]
}
});

2.7 DrawOption 配置项

  • editEnable boolean 是否允许编辑
  • selectEnable boolean 是否允许选中

2.8 方法

enable 开始编辑,绘制完成之后会自动结束。

1
draw.enable();

disable 结束编辑

1
draw.disable();

2.9 Style

  • active 绘制过程中高亮颜色
  • normal 正常显示状态
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
const style = {
active: {
point: {
type: 'PointLayer',
shape: 'circle',
color: '#fbb03b',
size: 5,
style: {
stroke: '#fff',
strokeWidth: 2,
},
},
line: {
type: 'LineLayer',
shape: 'line',
color: '#fbb03b',
size: 1,
style: {
opacity: 1,
lineType: 'dash',
dashArray: [2, 2],
},
},
polygon: {
shape: 'fill',
color: '#fbb03b',
style: {
opacity: 0.1,
stroke: '#fbb03b',
strokeWidth: 1,
strokeOpacity: 1,
lineType: 'dash',
dashArray: [2, 2],
},
},
},
normal: {
polygon: {
type: 'PolygonLayer',
shape: 'fill',
color: '#3bb2d0',
style: {
opacity: 0.1,
stroke: '#3bb2d0',
strokeWidth: 1,
strokeOpacity: 1,
lineType: 'solid',
dashArray: [2, 2],
},
},
line: {
type: 'LineLayer',
shape: 'line',
size: 1,
color: '#3bb2d0',
style: {
opacity: 1,
},
},
point: {
type: 'PointLayer',
shape: 'circle',
color: '#3bb2d0',
size: 3,
style: {
stroke: '#fff',
strokeWidth: 2,
},
},
},
normal_point: {
type: 'PointLayer',
shape: 'circle',
color: '#3bb2d0',
size: 3,
style: {
stroke: '#fff',
strokeWidth: 2,
},
},
mid_point: {
point: {
type: 'PointLayer',
shape: 'circle',
color: '#fbb03b',
size: 3,
style: {},
},
},
}

3. Calculate Type (新增功能)

可以不依赖 Draw UI 组件,独立的使用每一个 Calculate

  • getDistance 和 getArea 方法可用于距离和面积测量
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import { getDistance, getArea } from 'cci-lib-l7-draw'
/**
* 获取两个经纬度之间距离
* @param [lngLat.lng, lngLat.lat],
* @param [lngLat.lng, lngLat.lat]
* @returns string
*/
getDistance([120, 30], [121,30]) // km
/**
* 获取 polygon 的面积
* @param feature.geometry
* @returns string
*/
getArea({
type: 'Polygon',
coordinates: [
[
[44.29687499999999, 55.3791104480105],
[28.4765625, 47.754097979680026],
[27.0703125, 38.8225909761771],
[42.890625, 33.43144133557529],
[73.47656249999999, 37.43997405227057],
[85.4296875, 47.989921667414194],
[79.1015625, 60.58696734225869],
[44.29687499999999, 55.3791104480105]
]
]
}) // km^2

3.1 CalculateLine 测量距离

1
2
3
import { CalculateLine } from 'cci-lib-l7-draw';
const calculateLine = new CalculateLine(scene, option);
calculateLine.enable();
  • 事件

calculate-line-start 开始测量会派发

calculate-line-end 结束测量会派发

  • calculateLineOption 测量距离配置
name Type Default Description
style Object 框选样式
  • style 默认配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
{
line: {
type: 'LineLayer',
shape: 'line',
size: 1,
color: '#3bb2d0',
style: {
opacity: 1,
},
},
point: {
type: 'PointLayer',
shape: 'circle',
color: '#3bb2d0',
size: 3,
style: {
stroke: '#fff',
strokeWidth: 2,
},
},
}

3.2 CalculateArea 测量面积

1
2
3
import { CalculateArea } from 'cci-lib-l7-draw';
const calculateArea = new CalculateArea(scene, option);
calculateArea.enable();
  • 事件

calculate-area-start 开始测量会派发

calculate-area-end 结束测量会派发

  • calculateAreaOption 测量面积配置
name Type Default Description
style Object 框选样式
  • style 默认配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
{
polygon: {
type: 'PolygonLayer',
shape: 'fill',
color: '#3bb2d0',
style: {
opacity: 0.1,
stroke: '#3bb2d0',
strokeWidth: 1,
strokeOpacity: 1,
lineType: 'solid',
dashArray: [2, 2],
},
},
point: {
type: 'PointLayer',
shape: 'circle',
color: '#3bb2d0',
size: 3,
style: {
stroke: '#fff',
strokeWidth: 2,
},
},
}
  • 方法

enable 开始测量,在地图上点选,双击完成之后会自动结束。

1
calculate.enable();

disable 清除所有测量结果

1
calculate.disable();

4. RectZoom (新增功能)

矩形框选缩放,可以拉框选中,地图根据框选的位置为中心进行放大到最佳比例尺显示

1
2
import { RectZoom } from 'cci-lib-l7-draw'
const rectZoom = new RectZoom(scene, option)
  • rectZoomOption

框选配置

name Type Default Description
style Object 框选样式
step Number 1 每次缩放层级
  • style

默认配置

1
2
3
4
5
6
7
8
9
10
11
12
13
{
type: 'PolygonLayer',
shape: 'fill',
color: '#3bb2d0',
style: {
opacity: 0.1,
stroke: '#3bb2d0',
strokeWidth: 1,
strokeOpacity: 1,
lineType: 'solid',
dashArray: [2, 2],
},
}
  • 方法

enable 开始框选,框选完成之后会自动结束。

1
2
3
rectZoom.enable('zoomIn') // 放大
rectZoom.enable('zoomOut') // 缩小
rectZoom.enable() // 只框选
  • 事件

draw.create 绘制完成时触发该事件, 返回 feature 对应的数据

5. CircleZoom (新增功能)

圆形框选缩放,可以拉框选中,地图根据框选的位置为中心进行放大到最佳比例尺显示, 参数、方法和事件同 RectZoom

1
2
import { CircleZoom } from 'cci-lib-l7-draw'
const circleZoom = new CircleZoom(scene, option)

6. PolygonZoom (新增功能)

多边形框选缩放,可以拉框选中,地图根据框选的位置为中心进行放大到最佳比例尺显示, 参数、方法和事件同 RectZoom

1
2
import { PolygonZoom } from 'cci-lib-l7-draw'
const polygonZoom = new PolygonZoom(scene, option)

五、cci-map 可视化组件

@antv/l7 原有可视化图层和覆盖物( PointLayer, LineLayer, PolygonLayer, Marker, Popup, ImageLayer, RasterLayer, HeatmapLayer, CityBuildingLayer)可在初始化时通过 dataCoordinateSystem 参数指定数据的坐标系,内部会根据传入的数据坐标系,自动将数据转换为地图引擎需要的坐标系坐标数据, dataCoordinateSystem 参数不传则默认不做数据转换

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import { CMap, GaodeMap, PointLayer } from 'cci-map'
const scene = new CMap({
id: 'map',
map: new GaodeMap({
style: 'dark',
center: [ 112.345, 30.455 ],
zoom: 14.89,
minZoom: 10
})
});
scene.on('loaded', () => {
const pointLayer = new PointLayer({
dataCoordinateSystem: 'wgs84'
})
.source(
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Point",
"coordinates": [112.345, 30.455] // wgs84 坐标会转化为 gcj02 坐标
}
}
]
})
.color('red')

scene.addLayer(pointLayer);
})

1. 鹰眼组件

HawkEye 是鹰眼图组件

1.1 参数

参数 说明 类型 可选值 默认值
visible 控制鹰眼图显示隐藏 Boolean - true
sceneConfig 分别以 amap 或者 mapbox 地图实例的 scene 配置 Object
bigScene 外层地图 CMap 实例 Object
zoomGap 外层地图和鹰眼地图缩放级别之差 Number - 3

1.2 示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
<template>
<div style="width:100%;height:100%;">
<div id="map"/>
<HawkEye
ref="hawkEye"
:visible='show'
:bigScene="bigScene"
:sceneConfig="hawkEyeSceneConfig"
>
<div id="mini-map"></div>
</HawkEye>
</div>
</template>
<script>
import { CMap, GaodeMap, HawkEye } from 'cci-map'
// import { CMap, GaodeMap } from 'cci-map'
// import HawkEye from 'cci-cp-l7-hawk-eye'

const mapInstanceConfig = {
viewMode: '3D',
resizeEnable: true,
zoom: 12, // 初始化地图层级
pitch: 0,
rotation: 0,
center: [ 116.30470275878906, 39.88352811449648 ], // 初始化地图中心点
}

export default {
data() {
return {
show: true,
hawkEyeSceneConfig: {},
bigScene: {}
}
},
components: {
HawkEye
},
methods: {
toggle() {
this.show = !this.show
}
},
mounted() {
const scene = new CMap({
id: 'map',
map: new GaodeMap(mapInstanceConfig)
})
scene.on('loaded', () => {
this.hawkEyeSceneConfig = {
id: 'mini-map',
map: new GaodeMap(mapInstanceConfig)
}
this.bigScene = scene
})
}
}
</script>
<style>
#map {
position: absolute;
top: 0;
bottom: 0;
width: 100%;
}
#map .l7-scene {
position: absolute !important;
}
#mini-map{
position: absolute;
top: 0;
bottom: 0;
width: 100px;
height: 100px;
border: 1px solid black;
}
</style>

2. echarts 图层

如果 cci-map 使用 script 脚本引入,在使用 EchartsLayer 时需要先引入 echarts 库

2.1 parser 参数

调用图层 source 方法,第一个参数设置为 echarts 配置,第二个参数设置 parser 参数; 如果第一个参数为空对象, 之后通过调用图层 setData 方法传入 echarts 配置。echarts 配置中 series 配置必须通过 coordinateSystem 配置当前地图类型,地图类型可以通过 scene.mapService.getType 配置

1
2
3
4
5
{
parser: {
type: 'echarts'
}
}

2.2 示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
import { CMap, Mapbox, EchartsLayer } from 'cci-map'
// import { CMap, Mapbox } from 'cci-map'
// import { EchartsLayer } from 'cci-layers'
const scene = new CMap({
id: 'map',
map: new GaodeMap({
pitch: 30,
zoom: 10,
center: [120.264263, 30.14], // 初始化地图中心点
token: '48463d4ad0a6bd804a336d9ed317717e'
})
})

scene.on('loaded', async () => {
const data = '[{"type":"1-2","coords":[[{"coord":["120.273318","30.156055"],"type":"1"},{"coord":["120.278434","30.162534"],"type":"2"}],[{"coord":["120.285249","30.158228"],"type":"1"},{"coord":["120.28387","30.158531"],"type":"2"}],[{"coord":["120.223365","30.177374"],"type":"1"},{"coord":["120.229814","30.183639"],"type":"2"}],[{"coord":["120.23561","30.179176"],"type":"1"},{"coord":["120.234944","30.178863"],"type":"2"}],[{"coord":["120.263922","30.154831"],"type":"2"}],[{"coord":["120.281604","30.156878"],"type":"2"}],[{"coord":["120.273278","30.153959"],"type":"2"}],[{"coord":["120.283706","30.165705"],"type":"2"}],[{"coord":["120.255151","30.165263"],"type":"2"}],[{"coord":["120.282449","30.166185"],"type":"1"},{"coord":["120.282677","30.165139"],"type":"2"}],[{"coord":["120.269503","30.15329"],"type":"1"},{"coord":["120.269503","30.15329"],"type":"2"}],[{"coord":["120.255637","30.149547"],"type":"2"}],[{"coord":["120.236102","30.165584"],"type":"1"},{"coord":["120.236102","30.165584"],"type":"2"}],[{"coord":["120.23914","30.159275"],"type":"1"},{"coord":["120.23914","30.159275"],"type":"2"}]]},{"type":"2-3","coords":[[{"coord":["120.278434","30.162534"],"type":"2"},{"coord":["120.270583","30.100995"],"type":"3"}],[{"coord":["120.278434","30.162534"],"type":"2"},{"coord":["120.271154","30.077699"],"type":"3"}],[{"coord":["120.28387","30.158531"],"type":"2"},{"coord":["120.270583","30.100995"],"type":"3"}],[{"coord":["120.28387","30.158531"],"type":"2"},{"coord":["120.271154","30.077699"],"type":"3"}],[{"coord":["120.229814","30.183639"],"type":"2"},{"coord":["120.270583","30.100995"],"type":"3"}],[{"coord":["120.229814","30.183639"],"type":"2"},{"coord":["120.271154","30.077699"],"type":"3"}],[{"coord":["120.234944","30.178863"],"type":"2"},{"coord":["120.270583","30.100995"],"type":"3"}],[{"coord":["120.234944","30.178863"],"type":"2"},{"coord":["120.271154","30.077699"],"type":"3"}],[{"coord":["120.263922","30.154831"],"type":"2"},{"coord":["120.270583","30.100995"],"type":"3"}],[{"coord":["120.263922","30.154831"],"type":"2"},{"coord":["120.271154","30.077699"],"type":"3"}],[{"coord":["120.281604","30.156878"],"type":"2"},{"coord":["120.270583","30.100995"],"type":"3"}],[{"coord":["120.281604","30.156878"],"type":"2"},{"coord":["120.271154","30.077699"],"type":"3"}],[{"coord":["120.273278","30.153959"],"type":"2"},{"coord":["120.270583","30.100995"],"type":"3"}],[{"coord":["120.273278","30.153959"],"type":"2"},{"coord":["120.271154","30.077699"],"type":"3"}],[{"coord":["120.283706","30.165705"],"type":"2"},{"coord":["120.270583","30.100995"],"type":"3"}],[{"coord":["120.283706","30.165705"],"type":"2"},{"coord":["120.271154","30.077699"],"type":"3"}],[{"coord":["120.255151","30.165263"],"type":"2"},{"coord":["120.270583","30.100995"],"type":"3"}],[{"coord":["120.255151","30.165263"],"type":"2"},{"coord":["120.271154","30.077699"],"type":"3"}],[{"coord":["120.282677","30.165139"],"type":"2"},{"coord":["120.270583","30.100995"],"type":"3"}],[{"coord":["120.282677","30.165139"],"type":"2"},{"coord":["120.271154","30.077699"],"type":"3"}],[{"coord":["120.269503","30.15329"],"type":"2"},{"coord":["120.270583","30.100995"],"type":"3"}],[{"coord":["120.269503","30.15329"],"type":"2"},{"coord":["120.271154","30.077699"],"type":"3"}],[{"coord":["120.255637","30.149547"],"type":"2"},{"coord":["120.270583","30.100995"],"type":"3"}],[{"coord":["120.255637","30.149547"],"type":"2"},{"coord":["120.271154","30.077699"],"type":"3"}],[{"coord":["120.236102","30.165584"],"type":"2"},{"coord":["120.270583","30.100995"],"type":"3"}],[{"coord":["120.236102","30.165584"],"type":"2"},{"coord":["120.271154","30.077699"],"type":"3"}],[{"coord":["120.23914","30.159275"],"type":"2"},{"coord":["120.270583","30.100995"],"type":"3"}],[{"coord":["120.23914","30.159275"],"type":"2"},{"coord":["120.271154","30.077699"],"type":"3"}]]}]'

const lines = JSON.parse(data)
const layer = new EchartsLayer()
.source(
{},
{
parser: {
type: 'echarts'
}
}
)
scene.addLayer(layer)

layer.setData({
radiusAxis: {
show: false
},
angleAxis: {
type: "value",
show: false
},
series: lines.map(({ type, coords }) => {
return {
type: "lines",
animation: false,
zlevel: 999,
coordinateSystem: scene.mapService.getType(), // series 必须配置当前地图类型,通过 scene.mapService.getType 方法获取
lineStyle: {
color: type === "1-2" ? "#5ad8a6" : "#a6c84c",
width: 1,
opacity: 0.6,
curveness: 0.5
},
effect: {
show: true,
symbol: "triangle",
symbolSize: 10
},
data: coords.filter(line => line.length === 2)
}
})
})
})

3. 自定义图层

通过自定义图层用户可以绘制 canvas,svg 和 dom

  • 初始化参数

    | 参数 | 说明 | 类型 | 可选值 | 默认值 |
    | :—– | :———————————————— | :——- | :—– | :—– |
    | dom | 绘制元素,可以 canvas 节点、dom 节点或者 svg 节点 | | - | - |
    | render | 渲染方法, 在地图初始化和地图拖拽缩放时调用 | Function | | |

  • parser 参数(调用图层 source 方法,第一个参数设置为空对象,第二个参数设置 parser 参数)

    1
    2
    3
    4
    5
    {
    parser: {
    type: 'custom'
    }
    }

3.1 canvas

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
import { CMap, GaodeMap, CustomLayer } from 'cci-map'
// import { CMap, GaodeMap } from 'cci-map'
// import { CustomLayer } from 'cci-layers'
const center = [120.26, 30.14]
const scene = new CMap({
id: 'map',
map: new GaodeMap({
pitch: 30,
zoom: 10,
center, // 初始化地图中心点
token: '48463d4ad0a6bd804a336d9ed317717e'
})
})
scene.on('loaded', () => {
// 1. 设置 canvas 元素和 render 方法
const pos = scene.lngLatToContainer(center)
const canvas = document.createElement('canvas')
function render() {
const ctx = canvas.getContext("2d")
ctx.fillStyle = '#08f'
ctx.strokeStyle = '#fff'
ctx.beginPath()
const r = 50
ctx.moveTo(pos.x + r, pos.y)
ctx.arc(pos.x, pos.y, r, 0, 2 * Math.PI)
ctx.lineWidth = 6
ctx.closePath()
ctx.stroke()
ctx.fill()
}
// 2. 加载自定义 canvas 图层
const layer = new CustomLayer({
dom: canvas,
render
})
.source({}, {
parser: {
type: 'custom'
}
})
scene.addLayer(layer)
}

3.2 svg

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
import { CMap, GaodeMap, CustomLayer } from 'cci-map'
// import { CMap, GaodeMap } from 'cci-map'
// import { CustomLayer } from 'cci-layers'
const center = [120.26, 30.14]
const scene = new CMap({
id: 'map',
map: new GaodeMap({
pitch: 30,
zoom: 10,
center, // 初始化地图中心点
token: '48463d4ad0a6bd804a336d9ed317717e'
})
})
scene.on('loaded', () => {
// 1. 设置 svg 元素和 render 方法
const pos = scene.lngLatToContainer(center)
const canvas = document.createElementNS('http://www.w3.org/2000/svg', 'svg')
canvas.setAttribute('xmlns', 'http://www.w3.org/2000/svg')
const path = document.createElementNS('http://www.w3.org/2000/svg', 'path')
path.onclick = () => { console.log('svg path clicked') }
var styleText = []
styleText.push('stroke:red')
styleText.push('fill:green')
styleText.push('fill-opacity:0.3')
path.style.cssText = styleText.join(';')
canvas.appendChild(path)

function buildPath() {
var paths = []
const { x, y } = pos
for (let k = 0; k < 6; k += 1) {
var ag = Math.PI * 60 / 180 * k
const centerX = x + Math.cos(ag) * 50
const centerY = y + Math.sin(ag) * 50
paths.push((k === 0 ? 'M' : 'L') + centerX + ' ' + centerY)
}
return paths.join(' ') + ' Z'
}
function render() {
var newPath = buildPath()
path.setAttribute('d', newPath)
}
// 2. 加载自定义 svg 图层
const layer = new CustomLayer({
dom: canvas,
render
})
.source({}, {
parser: {
type: 'custom'
}
})
scene.addLayer(layer)
}

3.3 dom

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import { CMap, GaodeMap, CustomLayer } from 'cci-map'
// import { CMap, GaodeMap } from 'cci-map'
// import { CustomLayer } from 'cci-layers'
const center = [120.26, 30.14]
const scene = new CMap({
id: 'map',
map: new GaodeMap({
pitch: 30,
zoom: 10,
center, // 初始化地图中心点
token: '48463d4ad0a6bd804a336d9ed317717e'
})
})
scene.on('loaded', () => {
// 1. 设置 dom 元素和 render 方法
const canvas = document.createElement('div')
function render() {
canvas.style.background = 'rgba(0, 0, 0, 0.7)'
}
// 2. 加载自定义 dom 图层
const layer = new CustomLayer({
dom: canvas,
render
})
.source({}, {
parser: {
type: 'custom'
}
})
scene.addLayer(layer)
}

4. wms/wmts 图层

将瓦片图层加载到地图上

默认支持 EPSG:3857, 还支持 EPSG:4326

4.1 初始化参数

参数 说明 类型 可选值 默认值
minZoom 图层最小缩放层级 Number - 3
maxZoom 图层最大缩放层级 Number - 19

4.2 wms url 请求参数

1
2
3
4
5
6
7
8
9
10
{
url: 'https://img.nj.gov/imagerywms/Natural2015', // url 前缀
params: {
layers: 'Natural2015', // layers 图层名
version: '1.1.1', // version 版本号
format: 'image/png', // format 格式
transparent: true, // transparent 是否背景透明
styles: '' // styles 样式名,默认为空
}
}

4.3 wmts url 请求参数

1
2
3
4
5
6
7
8
9
10
{
url: 'https://img.nj.gov/imagerywms/Natural2015', // url 前缀
params: {
Layers: 'Natural2015', // layers 图层名
Version: '1.1.1', // version 版本号
Format: 'image/png', // format 格式
Transparent: true, // transparent 是否背景透明
Styles: '' // styles 样式名,默认为空
}
}

4.4 parser 参数

调用图层 source 方法,第一个参数配置 wms/wmts url 请求参数,第二个参数设置 parser 参数

1
2
3
4
5
6
7
{
parser: {
crs: 'EPSG:3857' // crs 默认支持 3857,还支持 'EPSG:4326'
type: 'wms' // wms 图层设置 type: 'wms'
// type: 'wmts' // wmts 图层设置 type: 'wmts'
}
}

4.5 示例

  • wms
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
import { CMap, Mapbox, WMSLayer } from 'cci-map'
// import { CMap, Mapbox } from 'cci-map'
// import { WMSLayer } from 'cci-layers'
// 如果 wmts 引入 WMTSLayer
const scene = new CMap({
id: 'map',
map: new Mapbox({
style: 'mapbox://styles/mapbox/streets-v11',
center: [-74.5447, 40.6892],
zoom: 8,
token: 'pk.eyJ1IjoicXdlcnJnaGoiLCJhIjoiY2p5cXY5aGtuMDQwMjNkbGdjYTJ5MWIxeSJ9.2ro1n4MvUCzJiV1m4mrxAA'
})
})

scene.on('loaded', () => {
// 如果 wmts, new WMTSLayer()
const layer = new WMSLayer({
minZoom: 5,
maxZoom: 18,
tileSize: 256
})
layer.source(
{
url: 'https://img.nj.gov/imagerywms/Natural2015',
params: {
layers: 'Natural2015',
version: '1.1.1',
format: 'image/png',
transparent: true
}
},
{
parser: {
type: 'wms', // type: 'wmts' 如果是 wmts 设置
}
}
)
scene.addLayer(layer)
})
  • wmts 4326
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import { Scene, Mapbox, WMTSLayer } from 'cci-map'
// import { Scene, Mapbox } from 'cci-map'
// import { WMTSLayer } from 'cci-layers'
const center = [105.08052356963802, 36.04231948670001]
const scene = new Scene({
id: 'map',
map: new Mapbox({
pitch: 0,
center,
zoom: 4
})
})
scene.on('loaded', () => {
const layer = new WMTSLayer()
layer.source(
{
url: 'https://t2.tianditu.gov.cn/vec_c/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=vec&STYLE=default&TILEMATRIXSET=c&FORMAT=tiles&TILECOL={x}&TILEROW={y}&TILEMATRIX={z}&tk=81559dbe079ce57eebaff4a81ca226df',
params: {}
},
{
parser: {
type: 'wmts',
crs: 'EPSG:4326'
}
}
)
scene.addLayer(layer)
})

5. 3d tiles 图层

加载 3d tiles 图层

  • 需首先加载 three 库 和 gltfLoader 库

  • 地图底图 mapbox 只支持 EPSG:3857 的 tileset.json 文件

5.1 初始化参数

参数 说明 类型 可选值 默认值
url tileset.json 的地址 String - -

5.2 parser 参数

调用图层 source 方法,第一个参数配置空对象,第二个参数设置 parser 参数

1
2
3
4
5
{
parser: {
type: '3dtiles'
}
}

5.3 示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import { CMap, GaodeMapV2, Tiles3DLayer } from 'cci-map'
// import { CMap, GaodeMapV2 } from 'cci-map'
// import { Tiles3DLayer } from 'cci-layers'
const scene = new CMap({
id: 'map',
map: new GaodeMapV2({
style: 'dark',
center: [121.502325, 31.238165],
pitch: 90,
rotation: -50,
zoom: 16
})
})

scene.on('loaded', () => {
const layer = new Tiles3DLayer({
url: 'https://a.amap.com/jsapi_demos/static/data3d/single.json' // 3d Tiles 入口文件
}).source({}, {
parser: {
type: '3dtiles'
}
})

scene.addLayer(layer)
})

6. 轨迹回放组件

RoutePlay 是轨迹回放组件

6.1 参数

参数 说明 类型 必传 默认值
scene 实例化场景 Object 是
fastSpeed 快速速度配置(正整数) Number 3
normalSpeed 正常速度配置(正整数) Number 2
lowSpeed 慢速速度配置(正整数) Number 1
isShowRouteLayer 是否显示轨迹 Boolean true
isShowVisitedLayer 是否显示实时经过轨迹 Boolean true
isShowMarker 是否显示marker(随轨迹运动点) Boolean true
marker 可外部传入 marker Object
routeLayer 可外部传入 轨迹图层(LineLayer 实例) Object
visitedLayer 可外部传入 实时经过轨迹图层(LineLayer 实例) Object
route 轨迹标准 geojson 数据 Object 是
size 轨迹线宽 Number 4
color 轨迹颜色 String #2896f6
visitedSize 实时经过轨迹线宽 Number 4
visitedColor 实时经过轨迹颜色 String #374048
markerEventList marker 事件列表(数组,’mousemove’,’click’,’mousedown’,’mouseup’,’dblclick’,’contextmenu’,’mouseover’,’mouseout’) Array [‘click’]
routeEventList 轨迹图层事件列表(数组,’mousemove’,’click’,’mousedown’,’mouseup’,’dblclick’,’contextmenu’,’mouseover’,’mouseout’) Array [‘click’]
visitedEventList 实时经过轨迹图层 事件列表(数组,’mousemove’,’click’,’mousedown’,’mouseup’,’dblclick’,’contextmenu’,’mouseover’,’mouseout’) Array [‘click’]

6.2 方法

start

开始播放

pause

暂停

stop

停止

reset

重置,播放速度为 normalSpeed

speedUp

设置速度为 fastSpeed

speedDown

设置速度为 lowSpeed

seekTo

跳转执行位置播放

参数:num 0-1之间小数

6.3 示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
<script>
import { CMap, GaodeMap, RoutePlay } from 'cci-map'
// import { CMap, GaodeMap } from 'cci-map'
// import RoutePlay from "cci-cp-l7-route"

export default {
components: {
RoutePlay
},
data() {
return {
scene: null,
routeData: {
type: "FeatureCollection",
features: [
{
type: "Feature",
properties: {},
geometry: {
type: "LineString",
coordinates: [
[105.6005859375, 30.65681556429287],
[107.95166015624999, 31.98944183792288],
[107.95166015624999, 31.98944183792288],
[109.3798828125, 30.031055426540206],
[107.7978515625, 29.935895213372444]
]
}
}
]
}
}
},
mounted() {
const scene = new CMap({
id: 'map',
map: new GaodeMap({
center: [106.790327, 31.095636],
pitch: 0,
zoom: 7,
style: 'light'
})
})
},
methods: {
start() {
this.$refs.route && this.$refs.route.start()
},
pause() {
this.$refs.route && this.$refs.route.pause()
},
stop() {
this.$refs.route && this.$refs.route.stop()
},
reset() {
this.$refs.route && this.$refs.route.reset()
},
speedUp() {
this.$refs.route && this.$refs.route.speedUp()
},
speedDown() {
this.$refs.route && this.$refs.route.speedDown()
},
seekTo() {
this.$refs.route && this.$refs.route.seekTo(0.5)
}
}
}
</script>
<template>
<div id="map" style="width:100%;height:100%;position: absolute;top: 0;bottom: 0;">
<RoutePlay
ref="route"
:scene="scene"
:route="routeData"
/>
<div class="buttons">
<button @click="start">
开始
</button>
<button @click="pause">
暂停
</button>
<button @click="stop">
结束
</button>
<button @click="reset">
重置
</button>
<button @click="speedUp">
加速
</button>
<button @click="speedDown">
减速
</button>
<button @click="seekTo">
指定 50% 位置播放
</button>
</div>
</div>
</template>
<style scoped>
.buttons{
position: absolute;
z-index: 1;
display: flex;
}
button{
margin-right: 5px;
}
</style>

六、图层发布和使用

  • 对于标准天地图,使用 cci-map 的 Mapbox
  • 对于天地图自定义坐标系,使用 cci-map 的 maptalks 结合 proj4 实现自定义天地图投影坐标系,可在地图基础上叠加 cci-map 可视化图层和 wmts/wms 图层

1. 对接流程

2. 图层发布后对接实例

自定义天地图投影坐标系目前使用 leaflet 实现(项目需要加载 leaflet,proj4, proj4leaflet 库文件),cci-map 会在下一个版本接入 maptalks 引擎,届时可以通过 maptalks 结合 proj4 实现自定义天地图投影坐标系

  • 页面源码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
<template>
<div id="map" />
</template>
<script>
import { CMap, GaodeMap, Mapbox, MapTalks, WMSLayer, CoordsTransform } from '@cci/map'
// import { CMap, GaodeMap, Mapbox, MapTalks } from 'cci-map'
// import { WMSLayer } from 'cci-layers'
// import { CoordsTransform } from 'cci-utils'
import mapboxgl from 'mapbox-gl'
window.mapboxgl = mapboxgl

import * as maptalks from 'maptalks'
import proj4 from 'proj4'

function isJSON(val) {
if (typeof val !== 'string') {
return false
}
try {
const obj = JSON.parse(val)
if (Object.prototype.toString.call(obj) === '[object Object]') {
return true
} else {
return false
}
} catch (e) {
return false
}
}

function getQuery() {
const formattedParams = {}
let params = location.href.split("?")[1] || ''
params = params.split("&")
for (let i = 0; i < params.length; i++) {
formattedParams[params[i].split("=")[0]] = params[i].split("=")[1]
}
return formattedParams
}

const BAIDU_MAP_TYPE = 2
const GAODE_MAP_TYPE = 1
const MAPBOX_MAP_TYPE = 3

export default {
computed: {
layerInfo() {
const layerInfo = decodeURIComponent(getQuery().layerInfo || {})
return isJSON(layerInfo) ? JSON.parse(layerInfo) : layerInfo
},
baseMapType() {
return Number(this.layerInfo && this.layerInfo.baseMapType || 3)
},
layerName() {
return this.layerInfo && this.layerInfo.layerName || ''
},
token() {
return this.layerInfo && this.layerInfo.token || 1
},
bbox() {
return this.layerInfo && this.layerInfo.boundingBox || {}
},
crs() {
return this.bbox && this.bbox.crs || 'EPSG:3857'
}
},
mounted() {
this.initMap()
},
methods: {
fitBounds() {
if (Object.keys(this.bbox).length) {
const bbox = this.bbox
if (this.baseMapType === MAPBOX_MAP_TYPE) {
this.boudns = [[bbox.minY, bbox.minX], [bbox.maxY, bbox.maxX]]
this.map.fitBounds(this.bounds)
} else {
if (this.baseMapType === GAODE_MAP_TYPE) {
// 转为高德
this.bounds = [CoordsTransform.wgs84togcj02(bbox.minX, bbox.minY), CoordsTransform.wgs84togcj02(bbox.maxX, bbox.maxY)]
} else {
this.bounds = [[bbox.minX, bbox.minY], [bbox.maxX, bbox.maxY]]
}
this.scene.fitBounds(this.bounds)
}
}
},
createWMSLayer() {
const layerName = this.layerName
if (!layerName) return

if (this.baseMapType === MAPBOX_MAP_TYPE) {
L.tileLayer.wms(`/withauthV2/${layerName}/${this.token}/wms`, {
layers: layerName,
transparent: true,
styles: '',
format: 'image/png',
version: '1.1.1',
srs: this.crs
}).addTo(this.map)
} else {
const layer = new WMSLayer({
minZoom: 5,
maxZoom: 18,
tileSize: 256
})
layer.source(
{
url: `/withauthV2/${layerName}/${this.token}/wms`,
params: {
layers: layerName,
transparent: true,
styles: '',
format: 'image/png',
version: '1.1.1'
}
},
{
parser: {
type: 'wms',
crs: this.crs
}
}
)
this.scene.addLayer(layer)
}

// const layer = new WMSLayer({
// minZoom: 5,
// maxZoom: 18,
// tileSize: 256
// })
// layer.source(
// {
// url: `/withauthV2/${layerName}/${this.token}/wms`,
// params: {
// layers: layerName,
// transparent: true,
// styles: '',
// format: 'image/png',
// version: '1.1.1'
// }
// },
// {
// parser: {
// type: 'wms',
// crs: this.crs
// }
// }
// )
// this.scene.addLayer(layer)
},
initMap() {
const center = [120.153576, 30.287459]// 默认中心点
const zoom = 11 // 默认缩放层级

this.scene = null

if (this.baseMapType === GAODE_MAP_TYPE) {
this.scene = new CMap({
id: 'map',
map: new GaodeMap({
zoom, // 初始化地图层级
center, // 初始化地图中心点
token: '48463d4ad0a6bd804a336d9ed317717e'
})
})
this.scene.on('loaded', () => {
this.fitBounds()
this.createWMSLayer()
})
} else if (this.baseMapType === BAIDU_MAP_TYPE) {
mapboxgl.accessToken = 'pk.eyJ1IjoicXdlcnJnaGoiLCJhIjoiY2p5cXY5aGtuMDQwMjNkbGdjYTJ5MWIxeSJ9.2ro1n4MvUCzJiV1m4mrxAA'
const map = new mapboxgl.Map({
container: 'map',
style: {
"name": "mapbox_baidu",
'version': 8,
'sources': {},
'layers': []
},
center,
zoom
})
this.scene = new CMap({
id: 'map',
map: new Mapbox({
mapInstance: map
})
})
this.scene.on('loaded', async() => {
const module = await import('./baiduLayer')
const BaiduLayer = module.default
map.addLayer(new BaiduLayer())
this.fitBounds()
this.createWMSLayer()
})
} else {
// 自定义投影坐标系天地图
const CRS_4490 = new L.Proj.CRS(
"EPSG:4490",
"+proj=longlat +ellps=GRS80 +no_defs",
{
resolutions: [
0.00549933137239034, // Level 0
0.00274966568619517, // Level 1
0.00137483284309758, // Level 2
0.000687416421548792, // Level 3
0.000343708210774396, // Level 4
0.000171854105387198,
8.5927052693599e-5,
4.29635263467995e-5,
2.14817631733998e-5,
1.07408815866999e-5,
5.37044079334994e-6,
2.68522039667497e-6,
1.34261019833748e-6
],
origin: [118.122911693886, 31.2869311022836]
}
)
this.map = L.map("map", {
minZoom: 1,
maxZoom: 20,
center: [30.0869311022836, 119.822911693886],
zoom: 1,
zoomControl: true,
attributionControl: true,
crs: CRS_4490,
editable: true
})
const url =
"http://172.18.109.232:8080/E36CCEA93443D1495DB9B9F2B2FFE348CB1A367D75176F815040AB19E54CDCA5DAAA25813AF965E2ABD0CC2463DD1223/PBS/rest/services/hzsyraster/Mapserver/"
const basemap = new L.TileLayer(url + "/tile/{z}/{y}/{x}", {
tileSize: 256,
maxZoom: 12
})
this.map.addLayer(basemap)

this.map.on('load', () => {
this.fitBounds()
this.createWMSLayer()
})

// const proj4490 = "+proj=longlat +ellps=GRS80 +no_defs"
// const projection = proj4('WGS84', proj4490)
// const spatialReference = {
// projection: {
// code: 'EPSG4490',
// project: function(c) {
// const pc = projection.forward(c.toArray())
// return new maptalks.Coordinate(pc)
// },
// unproject: function(pc) {
// const c = projection.inverse(pc.toArray())
// return new maptalks.Coordinate(c)
// }
// }, // geo projection, defined by proj4js
// resolutions: [
// 0.00549933137239034, // Level 0
// 0.00274966568619517, // Level 1
// 0.00137483284309758, // Level 2
// 0.000687416421548792, // Level 3
// 0.000343708210774396, // Level 4
// 0.000171854105387198,
// 8.5927052693599e-5,
// 4.29635263467995e-5,
// 2.14817631733998e-5,
// 1.07408815866999e-5,
// 5.37044079334994e-6,
// 2.68522039667497e-6,
// 1.34261019833748e-6
// ],
// 'fullExtent': {
// 'top': 90,
// 'left': -180,
// 'bottom': -90,
// 'right': 180
// }
// }

// const map = new maptalks.Map('map', {
// center,
// zoom,
// spatialReference,
// baseLayer: new maptalks.TileLayer('base', {
// urlTemplate: 'http://183.134.200.102:8080/DD1223/E36CCEA93443D1495DB9B9F2B2FFE348CB1A367D75176F815040AB19E54CDCA5DAAA25813AF965E2ABD0CC2463DD1223/PBS/rest/services/hzsyvector/Mapserver/tile/{z}/{y}/{x}',
// spatialReference,
// tileSystem: [1, -1, 118.122911693886, 31.2869311022836]
// })
// })

// this.scene = new CMap({
// id: 'map',
// map: new MapTalks({
// mapInstance: map
// })
// })
// this.scene.on('loaded', async() => {
// this.fitBounds()
// this.createWMSLayer()
// })
}
}
}
}
</script>
<style scoped>
::-webkit-scrollbar {
display: none;
}
#map {
width: 100%;
height: 99.5%;
}
</style>
  • 页面函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 变量说明
baseMapType 底图类型:1-高德 2-百度 3-天地图
layerName 图层名称
token 图层token

// 函数说明
// 初始化地图
function initMap()

// 获取图层边界,地图自适应边界
function fitBounds()

// 加载 wms 图层
function createWMSLayer()
  • 页面源码依赖的百度 layer 代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
import { CoordsTransform } from 'cci-map'
// import { CoordsTransform } from 'cci-utils'
function Xa(a) {
return typeof a === "function"
}
function Wa(a) {
return typeof a === "number"
}
function Za(a) {
return typeof a === "string"
}
function Db(a) {
return typeof a !== "undefined"
}
function Eb(a) {
return typeof a === "object"
}
function H(a, b) {
isNaN(a) && (a = Hb(a), a = isNaN(a) ? 0 : a)
Za(a) && (a = parseFloat(a))
isNaN(b) && (b = Hb(b), b = isNaN(b) ? 0 : b)
Za(b) && (b = parseFloat(b))
this.lng = a
this.lat = b
}
H.eM = function(a) {
return a && a.lng <= 180 && a.lng >= -180 && a.lat <= 74 && a.lat >= -74
}
H.prototype.nb = function(a) {
return a && this.lat == a.lat && this.lng == a.lng
}
var p = null
var j = undefined

class BaiduProjection {
constructor() {
this.lP = 6370996.81
this.tG = [1.289059486E7, 8362377.87, 5591021, 3481989.83, 1678043.12, 0]
this.Eu = [75, 60, 45, 30, 15, 0]
this.rP = [
[1.410526172116255E-8, 8.98305509648872E-6, -1.9939833816331, 200.9824383106796, -187.2403703815547, 91.6087516669843, -23.38765649603339, 2.57121317296198, -0.03801003308653, 1.73379812E7],
[-7.435856389565537E-9, 8.983055097726239E-6, -0.78625201886289, 96.32687599759846, -1.85204757529826, -59.36935905485877, 47.40033549296737, -16.50741931063887, 2.28786674699375, 1.026014486E7],
[-3.030883460898826E-8, 8.98305509983578E-6, 0.30071316287616, 59.74293618442277, 7.357984074871, -25.38371002664745, 13.45380521110908, -3.29883767235584, 0.32710905363475, 6856817.37],
[-1.981981304930552E-8, 8.983055099779535E-6, 0.03278182852591, 40.31678527705744, 0.65659298677277, -4.44255534477492, 0.85341911805263, 0.12923347998204, -0.04625736007561, 4482777.06],
[3.09191371068437E-9, 8.983055096812155E-6, 6.995724062E-5, 23.10934304144901, -2.3663490511E-4, -0.6321817810242, -0.00663494467273, 0.03430082397953, -0.00466043876332, 2555164.4],
[2.890871144776878E-9, 8.983055095805407E-6, -3.068298E-8, 7.47137025468032, -3.53937994E-6, -0.02145144861037, -1.234426596E-5, 1.0322952773E-4, -3.23890364E-6, 826088.5]
]
this.qG = [
[-0.0015702102444, 111320.7020616939, 1704480524535203, -10338987376042340, 26112667856603880, -35149669176653700, 26595700718403920, -10725012454188240, 1800819912950474, 82.5],
[8.277824516172526E-4, 111320.7020463578, 6.477955746671607E8, -4.082003173641316E9, 1.077490566351142E10, -1.517187553151559E10, 1.205306533862167E10, -5.124939663577472E9, 9.133119359512032E8, 67.5],
[0.00337398766765, 111320.7020202162, 4481351.045890365, -2.339375119931662E7, 7.968221547186455E7, -1.159649932797253E8, 9.723671115602145E7, -4.366194633752821E7, 8477230.501135234, 52.5],
[0.00220636496208, 111320.7020209128, 51751.86112841131, 3796837.749470245, 992013.7397791013, -1221952.21711287, 1340652.697009075, -620943.6990984312, 144416.9293806241, 37.5],
[-3.441963504368392E-4, 111320.7020576856, 278.2353980772752, 2485758.690035394, 6070.750963243378, 54821.18345352118, 9540.606633304236, -2710.55326746645, 1405.483844121726, 22.5],
[-3.218135878613132E-4, 111320.7020701615, 0.00369383431289, 823725.6402795718, 0.46104986909093, 2351.343141331292, 1.58060784298199, 8.77738589078284, 0.37238884252424, 7.45]
]
}
w2(a, b) {
if (!a || !b) return 0
var c; var d; var a = this.Ab(a)
if (!a) return 0
c = this.Tk(a.lng)
d = this.Tk(a.lat)
b = this.Ab(b)
return !b ? 0 : this.Re(c, this.Tk(b.lng), d, this.Tk(b.lat))
}
Zo(a, b) {
if (!a || !b) return 0
a.lng = this.OD(a.lng, -180, 180)
a.lat = this.SD(a.lat, -74, 74)
b.lng = this.OD(b.lng, -180, 180)
b.lat = this.SD(b.lat, -74, 74)
return this.Re(this.Tk(a.lng), this.Tk(b.lng), this.Tk(a.lat), this.Tk(b.lat))
}
Ab(a) {
if (a === p || a === j) return new H(0, 0)
var b, c
b = new H(Math.abs(a.lng), Math.abs(a.lat))
for (var d = 0; d < this.tG.length; d++) {
if (b.lat >= this.tG[d]) {
c = this.rP[d]
break
}
}
a = this.qK(a, c)
return a = new H(a.lng.toFixed(6), a.lat.toFixed(6))
}
zb(a) {
if (a === p || a === j || a.lng > 180 || a.lng < -180 || a.lat > 90 || a.lat < -90) return new H(0, 0)
var b, c
a.lng = this.OD(a.lng, -180, 180)
a.lat = this.SD(a.lat, -74, 74)
b = new H(a.lng, a.lat)
for (var d = 0; d < this.Eu.length; d++) {
if (b.lat >= this.Eu[d]) {
c = this.qG[d]
break
}
}
if (!c) {
for (d = 0; d < this.Eu.length; d++) {
if (b.lat <= -this.Eu[d]) {
c = this.qG[d]
break
}
}
}
a = this.qK(a, c)
return a = new H(a.lng.toFixed(2), a.lat.toFixed(2))
}
qK(a, b) { // 百度坐标便宜
if (a && b) {
var c = b[0] + b[1] * Math.abs(a.lng)
var d = Math.abs(a.lat) / b[9]
var d = b[2] + b[3] * d + b[4] * d * d + b[5] * d * d * d + b[6] * d * d * d * d + b[7] * d * d * d * d * d + b[8] * d * d * d * d * d * d
var c = c * (a.lng < 0 ? -1 : 1)
var d = d * (a.lat < 0 ? -1 : 1)
return new H(c, d)
}
}
Re(a, b, c, d) {
return this.lP * Math.acos(Math.sin(c) * Math.sin(d) + Math.cos(c) * Math.cos(d) * Math.cos(b - a))
}
Tk(a) {
return Math.PI * a / 180
}
v4(a) {
return 180 * a / Math.PI
}
SD(a, b, c) {
b != p && (a = Math.max(a, b))
c != p && (a = Math.min(a, c))
return a
}
OD(a, b, c) {
for (; a > c;) a -= c - b
for (; a < b;) a += c - b
return a
}
}

export default class MapboxBaiduLayer_new {
constructor() {
this.id = 'mapbox_baidu_layer_new'
this.type = 'custom'
this.renderingMode = '2d'
this.allImages = {}
this.curImages = {}
this._tileSize = 256
this.renderImgs = []
this.oldRendAllImageMap = []
this._tileOnlineArr = ["http://online0.map.bdimg.com/tile/", "http://online1.map.bdimg.com/tile/", "http://online2.map.bdimg.com/tile/", "http://online3.map.bdimg.com/tile/", "http://online4.map.bdimg.com/tile/"]
this.vertCode =
'attribute vec2 a_position;' +
'attribute vec2 a_texCoord; ' +
'varying vec2 v_texCoord; ' +
"uniform mat4 u_matrix;" +
'void main() { ' +
"gl_Position = u_matrix * vec4(a_position, 0.0, 1.0);" +
'v_texCoord = a_texCoord; ' +
'} '

// 片段着色器的sourse
this.fragCode =
'precision mediump float; ' +
'uniform sampler2D u_image;' +
'varying vec2 v_texCoord;' +
'void main() { ' +
'gl_FragColor = texture2D(u_image, v_texCoord);' +
'}'
}
getTileUrl(row, col, level) {
var udt = "20180426"
var style = "pl"
var baseUrl = this._tileOnlineArr[Math.abs(col + row) % this._tileOnlineArr.length]
var url = baseUrl + "?qt=tile&x=" + col + "&y=" + row + "&z=" + level + "&styles=" + style + "&scaler=1&color_dep=32&colors=50&udt=" + udt
return url
}

/**
* 通过百度的行列号得到这个百度切片对应的wgs84的坐标范围
* @param {any} R
* @param {any} C
* @param {any} L
*/
getWgs84BoundByBaiduRCL(R, C, L) {
// 有纠偏
var tileM = Math.pow(2, 18 - L) * this._tileSize// 计算该层级下一张图片的大小
var minX = C * tileM
var minY = R * tileM
var maxX = minX + tileM
var maxY = minY + tileM
var baiduProjection = new BaiduProjection()
var baiduWgsSW = baiduProjection.Ab({ lng: minX, lat: minY })
var baiduWgsNE = baiduProjection.Ab({ lng: maxX, lat: maxY })
var baiduWgsSE = baiduProjection.Ab({ lng: minX, lat: maxY })
var baiduWgsNW = baiduProjection.Ab({ lng: maxX, lat: minY })
// console.log(minX + ":" + minY + ":" + baiduWgsSW.lng + ":" + baiduWgsSW.lat + ":" + maxX + ":" + maxY + ":" + baiduWgsNE.lng + ":" + baiduWgsNE.lat);
var guojiaSW = CoordsTransform.bd09togcj02(baiduWgsSW.lng, baiduWgsSW.lat)
var guojiaNE = CoordsTransform.bd09togcj02(baiduWgsNE.lng, baiduWgsNE.lat)
var guojiaSE = CoordsTransform.bd09togcj02(baiduWgsSE.lng, baiduWgsSE.lat)
var guojiaNW = CoordsTransform.bd09togcj02(baiduWgsNW.lng, baiduWgsNW.lat)

var wgs84SW = CoordsTransform.gcj02towgs84(guojiaSW[0], guojiaSW[1])
var wgs84NE = CoordsTransform.gcj02towgs84(guojiaNE[0], guojiaNE[1])
var wgs84SE = CoordsTransform.gcj02towgs84(guojiaSE[0], guojiaSE[1])
var wgs84NW = CoordsTransform.gcj02towgs84(guojiaNW[0], guojiaNW[1])
return {
sw: { lng: wgs84SW[0], lat: wgs84SW[1] },
ne: { lng: wgs84NE[0], lat: wgs84NE[1] },
se: { lng: wgs84SE[0], lat: wgs84SE[1] },
nw: { lng: wgs84NW[0], lat: wgs84NW[1] }
}
}

/**
* 通过一个界别和wgs84的经纬度得到在百度里面的行列号和lebel
* @param {any} level
* @param {any} wgs84p
*/
getBaiduRCLByWgs84LngLat(level, wgs84p) {
// 先将转成百度
var guojiaP = CoordsTransform.wgs84togcj02(wgs84p.lng, wgs84p.lat)// 转换成国标
var baiduP = CoordsTransform.gcj02tobd09(guojiaP[0], guojiaP[1])// 国标转百度 得到的 baiduP[0] 表示lng , baiduP[1] 表示lat
// 百度经纬度在转百度米坐标
var baiduXY = new BaiduProjection().zb({
lng: baiduP[0],
lat: baiduP[1]
})

var t = Math.pow(2, 18 - level)// 最顶层的一张照片是当前层级的多少张照片
var k = Math.pow(2, 18 - level) * this._tileSize// 计算每一个图片表示墨卡托坐标系的大小
var col = Math.floor(baiduXY.lng / k)// 地图切片的行号
var row = Math.floor(baiduXY.lat / k)// 地图切片的列号 纬度
return {
R: row,
C: col,
L: level
}
}

getBoundImages() { // 得到地图当前范围的切片信息
var old = new Date()
var bounds = this.map.getBounds()
var level = Math.ceil(this.map.getZoom()) + 1
if (level > 20) {
level = 20
}
var tileM = Math.pow(2, 18 - level) * this._tileSize// 计算每一个图片表示墨卡托坐标系的大小
var minRC = this.getBaiduRCLByWgs84LngLat(level, bounds._sw)
var maxRC = this.getBaiduRCLByWgs84LngLat(level, bounds._ne)
this.curImages = []
for (var temRow = minRC.R; temRow <= maxRC.R; temRow++) {
for (var temCol = minRC.C; temCol <= maxRC.C; temCol++) {
var infoStr = level + "_" + temRow + "_" + temCol
if (this.allImages[infoStr] == undefined) {
var wgs84Bound = this.getWgs84BoundByBaiduRCL(temRow, temCol, level)
var swWebgl = mapboxgl.MercatorCoordinate.fromLngLat(wgs84Bound.sw)
var neWebgl = mapboxgl.MercatorCoordinate.fromLngLat(wgs84Bound.ne)
var seWebgl = mapboxgl.MercatorCoordinate.fromLngLat(wgs84Bound.se)
var nwWebgl = mapboxgl.MercatorCoordinate.fromLngLat(wgs84Bound.nw)
var webglBound = {
sw: { lng: swWebgl.x, lat: swWebgl.y },
ne: { lng: neWebgl.x, lat: neWebgl.y },
se: { lng: seWebgl.x, lat: seWebgl.y },
nw: { lng: nwWebgl.x, lat: nwWebgl.y }
}
this.allImages[infoStr] = {
bounds: webglBound,
url: this.getTileUrl(temRow, temCol, level)
}
}
this.curImages.push(this.allImages[infoStr])
}
}
// console.log(new Date().getTime() - old);
}

// 创建一个这色器程序
creatProgram(gl, vertSourse, fragSourse) {
// 创建顶点着色器
var vertShader = gl.createShader(gl.VERTEX_SHADER)
gl.shaderSource(vertShader, vertSourse) // 为一个顶点着色器设定一个源
gl.compileShader(vertShader) // 编译一个顶点着色器

// 创建一个片段着色器
var fragShader = gl.createShader(gl.FRAGMENT_SHADER)
gl.shaderSource(fragShader, fragSourse) // 为片段着色器设定一个源
gl.compileShader(fragShader) // 编译片段着色器

// 创建一个画图的应用
var program = gl.createProgram()
gl.attachShader(program, vertShader) // 为这个应用添加顶点着色器
gl.attachShader(program, fragShader) // 为这个应用添加片段着色器

gl.linkProgram(program) // 链接一个应用
return program
}
//
render(gl, matrix) {
var _this = this
_this.rendAllImageMap = []
var program = this.creatProgram(gl, this.vertCode, this.fragCode)
// 设置纹理的Buffter
gl.useProgram(program)// 一个render里面最好只有一个program,不然层序会卡死
this.renderkey = Math.random()
var curenderkey = this.renderkey// 记录是不是同一次渲染,因为不是同一次渲染的话就不需要渲染已经过期的图片
_this.rendImages(gl, matrix, _this.oldRendAllImageMap, program)// 先渲染一次上回加载的图片
for (var imageObj of this.curImages) {
this.loadImage(imageObj, function(obj) {
if (curenderkey == _this.renderkey) { // 不是同一
_this.rendAllImageMap.push(obj)
if (_this.rendAllImageMap.length == _this.curImages.length) {
_this.rendImages(gl, matrix, _this.rendAllImageMap, program)
_this.oldRendAllImageMap = _this.rendAllImageMap// 将上一次渲染成功的图片进行保存,保持界面能够平缓过度
}
}
// 不是最新的渲染要求就不进行渲染
})
}
}

loadImage(imageObj, callback) {
var image = new Image()
image.crossOrigin = "anonymous"
image.src = imageObj.url // MUST BE SAME DOMAIN!!!
// 判断图片是否在缓存中
if (image.complete) {
imageObj.img = image
callback.call(image, imageObj)
return
}
// 图片加载到浏览器的缓存中回调函数
image.onload = function() {
imageObj.img = image
callback.call(image, imageObj)
}
}
// 渲染多张图片
rendImages(gl, matrix, images, program) {
for (var imageObj of images) {
var texcoordLocation = gl.getAttribLocation(program, "a_texCoord")// 获取着色器里面的文理坐标的变量的指针
gl.enableVertexAttribArray(texcoordLocation)// 激活这个指针
gl.bindBuffer(gl.ARRAY_BUFFER, this.texcoordBuffer)// 将缓冲区绑定到该指针上
gl.vertexAttribPointer(texcoordLocation, 2, gl.FLOAT, false, 0, 0)// 指定读取缓冲区的方式
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, imageObj.img)// 上传图像到纹理
var uImage = gl.getUniformLocation(program, "u_image")// 获取纹理对象的指针
gl.uniform1i(uImage, 0)// 因为上面激活TEXTURE0 表示第一个纹理,所以这个地方使用第一个纹理,但是索引是0

var positionBuffer = gl.createBuffer()
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer)
this.setImageBufferData(gl, imageObj)

var u_matrix = gl.getUniformLocation(program, "u_matrix")
gl.uniformMatrix4fv(u_matrix, false, matrix)// 设置作色器里面的变换矩阵

var positionLocation = gl.getAttribLocation(program, "a_position")
gl.enableVertexAttribArray(positionLocation)
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer)
gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0)
gl.drawArrays(gl.TRIANGLES, 0, 6)
}
}

setImageBufferData(gl, imageObj) {
var bounds = imageObj.bounds
var arr = [
bounds.sw.lng, bounds.sw.lat,
bounds.nw.lng, bounds.nw.lat,
bounds.se.lng, bounds.se.lat,
bounds.se.lng, bounds.se.lat,
bounds.nw.lng, bounds.nw.lat,
bounds.ne.lng, bounds.ne.lat
]
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(arr), gl.STATIC_DRAW)
}

onAdd(map, gl) {
this.map = map
this.createTextureBuffer(gl)
}
prerender(gl, matrix) {
this.getBoundImages()
}
createTextureBuffer(gl) {
// 创建文理的坐标的缓冲区
var texcoordBuffer = gl.createBuffer()// 创建一个缓冲区
gl.bindBuffer(gl.ARRAY_BUFFER, texcoordBuffer)// 指定该缓冲区为数组缓冲区
// 这个地方要注意图片颠倒问题
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
0.0, 1.0,
1.0, 1.0,
0.0, 0.0,
0.0, 0.0,
1.0, 1.0,
1.0, 0.0
]), gl.STATIC_DRAW)// 将具体的数组放到缓冲区里面
this.texcoordBuffer = texcoordBuffer

var texture = gl.createTexture()
gl.bindTexture(gl.TEXTURE_2D, texture)
// 设置参数,让我们可以绘制任何尺寸的图像
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE) // TEXTURE_WRAP_S 横向平铺的方式 CLAMP_TO_EDGE表明不重复
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE) // TEXTURE_WRAP_T 纵向平铺的方式
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST) // TEXTURE_MIN_FILTER 缩小的颜色选取方法 NEAREST就近取色,不进行颜色插值 速度快
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST) // TEXTURE_MAG_FILTER 放大的颜色选取方法
gl.activeTexture(gl.TEXTURE0)// 激活一个纹理
gl.bindTexture(gl.TEXTURE_2D, texture) // 绑定一个文理到gl.TEXTURE0
}
onRemove(map) {
}
}

class NullIslandLayer {
constructor() {
this.id = 'null-island'
this.type = 'custom'
this.renderingMode = '2d'
}

onAdd(map, gl) {
const vertexSource = `
uniform mat4 u_matrix;
void main() {
gl_Position = u_matrix* vec4(0.5, 0.5, 0.0, 1.0);
gl_PointSize = 200.0;
}`

const fragmentSource = `
void main() {
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}`

const vertexShader = gl.createShader(gl.VERTEX_SHADER)
gl.shaderSource(vertexShader, vertexSource)
gl.compileShader(vertexShader)
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER)
gl.shaderSource(fragmentShader, fragmentSource)
gl.compileShader(fragmentShader)

this.program = gl.createProgram()
gl.attachShader(this.program, vertexShader)
gl.attachShader(this.program, fragmentShader)
gl.linkProgram(this.program)
}

render(gl, matrix) {
gl.useProgram(this.program)
gl.uniformMatrix4fv(gl.getUniformLocation(this.program, "u_matrix"), false, matrix)
gl.drawArrays(gl.POINTS, 0, 1)
}
}

七、常用代码示例

1. CMap 属性、方法和事件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// Module 引用
import { CMap, GaodeMap } from 'cci-map'
const scene = new CMap({
id: 'map',
map: new GaodeMap({
style: 'dark',
center: [110.770672, 34.159869],
pitch: 45,
}),
});

// CDN 使用方法
const scene = new CciMap.CMap({
id: 'map',
map: new CciMap.GaodeMap({
style: 'dark',
center: [110.770672, 34.159869],
pitch: 45,
}),
})

可以通过 scene map 属性获取 map 实例

1
const map = scene.map;

为了统一不同底图之前的接口差异, 在 scene 层对 map 的方法做了统一,因此一些地图的操作方法可以通过 scene 调用这样,切换不同底图时保证表现一致

1
2
3
4
5
6
7
8
const scene = new CciMap.CMap({
id: 'map',
map: new CciMap.GaodeMap({
style: 'dark',
center: [110.770672, 34.159869],
pitch: 45,
}),
});

1.1 Map 配置项

  • zoom 初始化缩放等级

地图初始显示级别 {number} Mapbox (0-24) 高德 (2-19)

  • center 地图中心

地图初始中心经纬度 {Lnglat}

  • pitch 地图倾角

地图初始俯仰角度 {number} default 0

  • style 地图图样式

简化地图样式设置,内置了三种主题默认样式, 高德,mapbox, maptalks 都可以使用 (dark, light, normal, blank 无底图)

除了内置的样式,也可传入自定义的其他属性

比如高德地图 ⚠️ 高德地图样式 增加 isPublic=true 参数

1
2
3
{
style: 'amap://styles/2a09079c3daac9420ee53b67307a8006?isPublic=true'; // 设置方法和高德地图一致
}
  • minZoom 最小缩放等级

地图最小缩放等级 {number} default 0 Mapbox 0-24) 高德 (2-19)

  • maxZoom 最大缩放等级

地图最大缩放等级 {number} default 22 Mapbox(0-24) 高德 (2-19)

  • rotateEnable 是否允许旋转

地图是否可旋转 {Boolean} default true

1.2 Layer 方法

addLayer(layer) 增加图层对象

参数 :

  • layer {ILayer} 图层对象
1
scene.addLayer(layer);

getLayer(id) 获取对应的图层对象

参数 :

  • id {string}
1
scene.getLayer('layerID');

getLayers() 获取所有的地图图层

1
scene.getLayers();

getLayerByName(name) 根据图层名称获取图层

参数

  • name {string} layer 初始化可配置图层 name
1
scene.getLayerByName(name); // return Layer 图层对象

removeLayer 移除 layer 图层

参数 :

  • layer {Layer}
1
scene.removeLayer(layer);

removeAllLayer() 移除所有的图层对象

1
scene.removeAllLayer();

1.3 控制组件方法

addControl(ctl) 添加组件控件

参数 :

  • crl { IControl } 用户创建的控件对象
1
scene.addControl(ctl);

removeControl(ctr) 移除用户添加的组件控件

参数 :

  • ctl { IControl } 用户创建的控件对象
1
scene.removeControl(ctl);

getControlByName(name) 根据控件的名称来获取控件

  • name { string }
1
2
3
4
5
6
7
const zoomControl = new Zoom({
// zoom 控件
name: 'z1', // 用户传入的控件名称(也可以不传入,该控件默认名称为 zoom)
position: 'topright',
});

scene.getControlByName('z1');

1.4 标记方法

addMarker(maker) 往场景中添加标记对象

参数 :

  • maker { IMarker } Marker 实例
1
2
3
4
const marker = new Marker({
element: el,
}).setLnglat({ lng: nodes[i].x * 1, lat: nodes[i].y });
scene.addMarker(marker);

addMarkerLayer(layer) 添加 Marker 统一管理图层

当用户需要添加许多个 Marker 实例时,为了方便管理可以使用 markerLayer 对象统一管理

参数 :

  • layer { IMarkerLayer } 标记图层对象
1
2
const markerLayer = new MarkerLayer();
scene.addMarkerLayer(markerLayer);

removeMarkerLayer(layer) 移除标签图层

参数 :

  • layer { IMarkerLayer } 标记图层对象
1
scene.removeMarkerLayer(markerLayer);

removeAllMakers() 移除场景中所有的标签对象

1
scene.removeAllMakers();

1.4 地图方法

getZoom 获取当前缩放等级

1
scene.getZoom(); // return {float}  当前缩放等级

getCenter() 获取地图中心

1
scene.getCenter(); // return {Lnglat} :地图中心点

getSize() 获取地图容器大小

1
scene.getSize(); // return { Object } 地图容器的 width,height

getPitch() 获取地图倾角

1
scene.getPitch(); // return {number} pitch

getContainer 获取地图容器

1
scene.getContainer(); // return htmlElement

setMapStyle 设置地图样式

参数:style {string} 地图样式 具体样式格式和各底图设置方法一致

内置了三种地图样式,AMAP 和 MapBox, MapTalks 都适用

  • light
  • dark
  • normal
1
2
3
4
5
6
7
8
9
10
// 快捷名称设置
scene.setMapStyle('light');

// mapbox 主题设置
scene.setMapStyle('mapbox://styles/mapbox/streets-v11');

// AMap
scene.setMapStyle(
'amap://styles/2a09079c3daac9420ee53b67307a8006?isPublic=true',
);

setCenter() 设置地图中心点

参数:center {LngLat} 地图中心点

1
scene.setCenter([lng, lat]);

setZoomAndCenter 设置地图缩放等级和中心点

参数:

  • zoom {number}
  • center {LngLat}
1
scene.setZoomAndCenter(zoom, center);

setRotation 设置地图旋转

设置地图顺时针旋转角度,旋转原点为地图容器中心点,取值范围 [0-360]

参数: rotation {number}

1
scene.setRotation(rotation);

zoomIn 地图放大一级

1
scene.zoomIn();

zoomOut 地图缩小一级

1
scene.ZoomOUt();

panTo 地图平移到指定的位置

参数:

  • center LngLat 中心位置坐标
1
scene.panTo(LngLat);

panBy 地图平移

以像素为单位沿 X 方向和 Y 方向移动地图

参数:

  • x {number} 水平方向移动像素 向右为正方向
  • y {number} 垂直方向移动像素 向下为正方向
1
scene.panBy(x, y);

setPitch 设置地图仰俯角度

1
scene.setPitch(pitch);

setMapStatus 设置地图状态

可用来关闭地图的一些交互操作

参数 :

1
2
3
4
5
6
7
8
9
 IStatusOptions {
showIndoorMap: boolean;
resizeEnable: boolean;
dragEnable: boolean;
keyboardEnable: boolean;
doubleClickZoom: boolean;
zoomEnable: boolean;
rotateEnable: boolean;
scene.setMapStatus({ dragEnable: false });

fitBounds 设置地图缩放范围

地图缩放到某个范围内

参数 :

  • extent { array} 经纬度范围 [[minlng,minlat],[maxlng,maxlat]]
1
2
3
4
scene.fitBounds([
[112, 32],
[114, 35],
]);

containerToLngLat 画布坐标转经纬度

画布坐标转经纬度坐标

参数 :

  • pixel 画布的坐标 [x ,y ] {array }
1
scene.pixelToLngLat([10,10]);

lngLatToContainer 经纬度转画布坐标

参数 :

  • lnglat 经纬度坐标 [lng,lat ] {array }
1
scene.lngLatToPixel([120,10]);

pixelToLngLat 像素坐标转经纬度

像素坐标:不同级别下地图上某点的位置

参数 :

  • pixel 画布的坐标 [x ,y ] {array }
1
scene.pixelToLngLat([10,10]);

lngLatToPixel 经纬度转像素坐标

经纬度坐标转像素坐标

参数 :

  • lnglat 经纬度坐标 [lng,lat ] {array }
1
scene.lngLatToPixel([120,10]);

exportMap 导出地图图片

导出地图,目前仅支持导出可视化层,不支持底图导出

  • 参数 type png|jpg 默认 png
1
scene.exportMap('png');

destroy 场景销毁

scene 销毁方法,离开页面,或者不需要使用地图可以调用

1
scene.destroy();

1.5 iconfont 映射支持

addIconFont(name, fontUnicode) 增加对数据中 unicode 的映射支持

支持对用户传入的数据进行 unicode 的映射,在内部维护一组名称和对应 key 的键值对

参数 :

  • name {string}
  • fontUnicode {string}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
scene.addIconFont('icon1', '&#xe64b;');
scene.addIconFont('icon2', '&#xe64c;');
scene.addFontFace(fontFamily, fontPath);
const pointIconFontLayer = new PointLayer({})
.source(
[
{
j: 140,
w: 34,
m: 'icon1',
},
{
j: 140,
w: 36,
m: 'icon2',
},
],
{
parser: {
type: 'json',
x: 'j',
y: 'w',
},
},
)
.shape('m', 'text')
.size(12)
.color('w', ['#f00', '#f00', '#0f0'])
.style({
fontFamily,
iconfont: true,
textAllowOverlap: true,
});
scene.addLayer(pointIconFontLayer);

addIconFonts(options) 同时传入多组 name - unicode 的键值对

参数 :

  • options { Array<[name, unicode]> }
1
2
3
4
scene.addIconFonts([
['icon1', '&#xe64b;'],
['icon2', '&#xe64c;'],
]);

1.6 全局资源

addImage(id, img) 全局中添加的图片资源

参数 :

  • id {string}
  • img {HTMLImageElement | File | string}
1
2
3
4
scene.addImage(
'02',
'https://gw.alipayobjects.com/zos/bmw-prod/ce83fc30-701f-415b-9750-4b146f4b3dd6.svg',
);

hasImage(id) 判断是否已经在全局添加过相应的图片资源

参数 :

  • id {string}
1
scene.hasImage('imageID');

removeImage(id) 全局删除图片资源

参数 :

  • id {string}
1
scene.removeImage('imageID');

addFontFace(fontFamily, fontPath) 添加字体文件

参数 :

  • fontFamily {string} 用户为自己定义的字体名称
  • fontPath {string} 导入的文件地址
1
2
3
4
let fontFamily = 'iconfont';
let fontPath =
'//at.alicdn.com/t/font_2534097_iiet9d3nekn.woff2?t=1620444089776';
scene.addFontFace(fontFamily, fontPath);

1.7 事件

cci-map 针对不同地图的事件对象进行兼容,访问事件对象的经纬度 调用 getFormatEventObj 方法会返回含有 lng,lat 的经纬度对象

1
2
3
4
5
import { getFormatEventObj }  from 'cci-map'
// 初始化
scene.on('click', e => {
const { lng, lat } = getFormatEventObj(e) // e.lnglat
})
  • on 事件监听

eventName {string} 事件名 handler {function } 事件回调函数

  • off

移除事件监听 eventName {string} 事件名 handler {function } 事件回调函数

  • loaded

scene 初始化完成事件,scene 初始化完成添加 Layer

1
scene.on('loaded', () => {});
  • resize 地图容器变化事件
1
scene.on('resize', () => {}); // 地图容器大小改变事件
  • 地图事件
1
2
3
4
5
6
7
scene.on('loaded', () => {}); //地图加载完成触发
scene.on('mapmove', () => {}); // 地图平移时触发事件
scene.on('movestart', () => {}); // 地图平移开始时触发
scene.on('moveend', () => {}); // 地图移动结束后触发,包括平移,以及中心点变化的缩放。如地图有拖拽缓动效果,则在缓动结束后触发
scene.on('zoomchange', () => {}); // 地图缩放级别更改后触发
scene.on('zoomstart', () => {}); // 缩放开始时触发
scene.on('zoomend', () => {}); // 缩放停止时触发

其他地图事件可以查看相应底图的事件文档,地图事件也可以通过 CMap.map 进行设置 Mapbox 高德

  • 鼠标事件
1
2
3
4
5
6
7
8
9
10
11
12
scene.on('click', (ev) => {}); // 鼠标左键点击事件
scene.on('dblclick', (ev) => {}); // 鼠标左键双击事件
scene.on('mousemove', (ev) => {}); // 鼠标在地图上移动时触发
scene.on('mousewheel', (ev) => {}); // 鼠标滚轮开始缩放地图时触发
scene.on('mouseover', (ev) => {}); // 鼠标移入地图容器内时触发
scene.on('mouseout', (ev) => {}); // 鼠标移出地图容器时触发
scene.on('mouseup', (ev) => {}); // 鼠标在地图上单击抬起时触发
scene.on('mousedown', (ev) => {}); // 鼠标在地图上单击按下时触发
scene.on('contextmenu', (ev) => {}); // 鼠标右键单击事件
scene.on('dragstart', (ev) => {}); //开始拖拽地图时触发
scene.on('dragging', (ev) => {}); // 拖拽地图过程中触发
scene.on('dragend', (ev) => {}); //停止拖拽地图时触发。如地图有拖拽缓动效果,则在拽停止,缓动开始前触发

2 地球模式

Earth 相较于高德地图、mapbox 地图,是完全不同的一种表现形式,提供了全球视角下的可视化展示能力,为用户提供了更多的地理信息可视化表现形式。

✨ 为了区别普通的地图,提供了全新的 Earth 地图类型以及对应的 EarthLayer 图层

2.1 支持的图层类型

点图层

  • 平面点
  • 圆柱点

线图层

  • 3D 弧线

2.2 使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
// 1、引入对应模块
import { Earth, EarthLayer, CMap } from 'cci-map'
...
// 2、构建 Earth Map
const scene = new CMap({
id: 'map',
map: new Earth({}),
});
...
// 3、构建地球图层,当前的 shape 为 base,表示基础球体
const earthlayer = new EarthLayer()
.source(
// 地球表面的纹理
'https://gw.alipayobjects.com/mdn/rms_23a451/afts/img/A*3-3NSpqRqUoAAAAAAAAAAAAAARQnAQ',
{
parser: {
type: 'image',
extent: [121.168, 30.2828, 121.384, 30.421],
},
},
)
.color('#f00')
.shape('base')
.style({
opacity: 1.0,
radius: 40,
globelOtions: {
ambientRatio: 0.6, // 环境光
diffuseRatio: 0.4, // 漫反射
specularRatio: 0.1, // 高光反射
earthTime: 0.1,
},
})
.animate(true);

// 4、添加基础地球球体
scene.addLayer(earthlayer);

// 经过上述的步骤,我们就可以在场景中添加一个基础的地球了

2.3 独立的地图类型 Earth

2.3.1 构造函数 Earth(args)

作为 Earth 的基础地图类型,Earth 提供了地球系统的相机系统,目前只需要传入一个空对象。

  • args: {}
2.3.2 rotateY(option: { force: boolean; regScale: number})

提供了简单的方法控制地球系统的旋转(实际上控制的是相机的旋转)

  • force: false 判断是否强制生效,默认该方法的优先级比用户鼠标操作要低,当用户操作相机的时候,该方法会失效
  • regScale: 0.01 旋转的角度(视觉上地球的旋转角度), regScale 表示的并不是实际的旋转角度,而是单位旋转角度的比例
    🌟 单位旋转角度 = Math.min(this.earthCameraZoom * this.earthCameraZoom, 1)

2.4 地图图层 EarthLayer

地球图层区别于普通高德地图和 Mapbox 地图的图层,只在地球模式下可以被使用,用于表示地球的球体、大气层、辉光等效果。
🌟 使用不同的 shape 参数表示区别不同的地球图层

2.4.1 地球球体图层 baseLayer
  • source(map, parser) map: 地球表面纹理贴图的地址 parser: 解析器,目前只需要写固定的对象值即可 { parser: { type: “image” } }
  • shape: ‘base’
    🌟 目前支持的 shape 类型有 base、atomSphere、bloomSphere,当用户的 shape 参数不被识别时,自动降级为 base 类型
  • globelOtions: style 方法中的参数 ambientRatio: 环境光、diffuseRatio: 漫反射、specularRatio: 高光反射
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const earthlayer = new EarthLayer()
.source(
'https://gw.alipayobjects.com/mdn/rms_23a451/afts/img/A*3-3NSpqRqUoAAAAAAAAAAAAAARQnAQ',
{
parser: {
type: 'image',
},
},
)
.shape('base')
.style({
globelOtions: {
ambientRatio: 0.6, // 环境光
diffuseRatio: 0.4, // 漫反射
specularRatio: 0.1, // 高光反射
},
});
2.4.2 地球内发光/大气图层 atomLayer

atomLayer 作为地球的效果图层,不需要传入数据,所以可以不调用 source 方法

1
2
3
4
5
6
7
const atomLayer = new EarthLayer()
.color('#2E8AE6')
.shape('atomSphere')
.style({
// 可以控制发光程度
opacity: 1,
});
2.4.3 地球内外发光/辉光图层 bloomLayer

bloomLayer 作为地球的效果图层,不需要传入数据,所以可以不调用 source 方法

1
2
3
4
5
6
const bloomLayer = new EarthLayer()
.color('#fff')
.shape('bloomSphere')
.style({
opacity: 0.5,
});

2.5 示例代码

地球模式下使用飞线图层无需做额外的操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
import { CMap, EarthLayer, LineLayer, Earth } from 'cci-map';
// import { CMap, EarthLayer, Earth } from 'cci-map'
// import { LineLayer } from 'cci-layers'
const scene = new CMap({
id: 'map',
map: new Earth({})
});

// TODO: 地球模式下背景色默认为 #000 通过 setBgColor 方法我们可以设置可视化层的背景色
scene.setBgColor('#333');

const flydata = [{ coord: [[ 104.195397, 35.86166 ], [ 100.992541, 15.870032 ]] }, { coord: [[ 104.195397, 35.86166 ], [ 114.727669, 4.535277 ]] }, { coord: [[ 104.195397, 35.86166 ], [ 9.501785, 56.26392 ]] }, { coord: [[ 104.195397, 35.86166 ], [ -66.590149, 18.220833 ]] }, { coord: [[ 104.195397, 35.86166 ], [ 174.885971, -40.900557 ]] }, { coord: [[ 104.195397, 35.86166 ], [ 104.990963, 12.565679 ]] }, { coord: [[ 104.195397, 35.86166 ], [ 6.129582999999999, 49.815273 ]] }, { coord: [[ 104.195397, 35.86166 ], [ 8.468945999999999, 60.47202399999999 ]] }, { coord: [[ 104.195397, 35.86166 ], [ 108.277199, 14.058324 ]] }, { coord: [[ 104.195397, 35.86166 ], [ -95.712891, 37.09024 ]] }, { coord: [[ 104.195397, 35.86166 ], [ 121.49917, 25.12653 ]] }, { coord: [[ 104.195397, 35.86166 ], [ -9.429499000000002, 6.428055 ]] }, { coord: [[ 104.195397, 35.86166 ], [ 127.766922, 35.907757 ]] }, { coord: [[ 104.195397, 35.86166 ], [ 10.451526, 51.165691 ]] }, { coord: [[ 104.195397, 35.86166 ], [ 23.881275, 55.169438 ]] }, { coord: [[ 104.195397, 35.86166 ], [ 34.851612, 31.046051 ]] }, { coord: [[ 104.195397, 35.86166 ], [ 53.847818, 23.424076 ]] }, { coord: [[ 104.195397, 35.86166 ], [ 102.495496, 19.85627 ]] }, { coord: [[ 104.195397, 35.86166 ], [ 21.824312, 39.074208 ]] }, { coord: [[ 104.195397, 35.86166 ], [ 25.48583, 42.733883 ]] }, { coord: [[ 104.195397, 35.86166 ], [ 15.472962, 49.81749199999999 ]] }, { coord: [[ 104.195397, 35.86166 ], [ 78.96288, 20.593684 ]] }, { coord: [[ 104.195397, 35.86166 ], [ -3.435973, 55.378051 ]] }, { coord: [[ 104.195397, 35.86166 ], [ 33.429859, 35.126413 ]] }, { coord: [[ 104.195397, 35.86166 ], [ 8.227511999999999, 46.818188 ]] }, { coord: [[ 104.195397, 35.86166 ], [ 114.066662, 22.588638 ]] }, { coord: [[ 104.195397, 35.86166 ], [ 14.550072, 47.516231 ]] }, { coord: [[ 104.195397, 35.86166 ], [ 2.213749, 46.227638 ]] }, { coord: [[ 104.195397, 35.86166 ], [ 12.56738, 41.87194 ]] }, { coord: [[ 104.195397, 35.86166 ], [ 105.318756, 61.52401 ]] }, { coord: [[ 104.195397, 35.86166 ], [ 80.77179699999999, 7.873053999999999 ]] }, { coord: [[ 104.195397, 35.86166 ], [ 5.291265999999999, 52.132633 ]] }, { coord: [[ 104.195397, 35.86166 ], [ 35.243322, 38.963745 ]] }, { coord: [[ 104.195397, 35.86166 ], [ 18.643501, 60.12816100000001 ]] }, { coord: [[ 104.195397, 35.86166 ], [ 25.748151, 61.92410999999999 ]] }, { coord: [[ 104.195397, 35.86166 ], [ -3.74922, 40.46366700000001 ]] }, { coord: [[ 104.195397, 35.86166 ], [ 113.551538, 22.109432 ]] }, { coord: [[ 104.195397, 35.86166 ], [ 4.469936, 50.503887 ]] }, { coord: [[ 104.195397, 35.86166 ], [ -106.346771, 56.130366 ]] }, { coord: [[ 104.195397, 35.86166 ], [ 138.252924, 36.204824 ]] }, { coord: [[ 104.195397, 35.86166 ], [ 17.679076, 43.915886 ]] }, { coord: [[ 104.195397, 35.86166 ], [ -88.49765, 17.189877 ]] }, { coord: [[ 104.195397, 35.86166 ], [ 25.013607, 58.595272 ]] }, { coord: [[ 104.195397, 35.86166 ], [ 101.975766, 4.210484 ]] }, { coord: [[ 104.195397, 35.86166 ], [ -8.24389, 53.41291 ]] }, { coord: [[ 104.195397, 35.86166 ], [ -8.224454, 39.39987199999999 ]] }, { coord: [[ 104.195397, 35.86166 ], [ 133.775136, -25.274398 ]] }, { coord: [[ 104.195397, 35.86166 ], [ 121.774017, 12.879721 ]] }];
const flyLine = new LineLayer({ blend: 'normal' })
.source(flydata, {
parser: {
type: 'json',
coordinates: 'coord'
}
})
.color('#b97feb')
.shape('arc3d')
.size(0.5)
.active(true)
.animate({
interval: 2,
trailLength: 2,
duration: 1
})
.style({
opacity: 1,
segmentNumber: 60,
globalArcHeight: 20
});


const earthlayer = new EarthLayer()
.source(
'https://gw.alipayobjects.com/mdn/rms_23a451/afts/img/A*3-3NSpqRqUoAAAAAAAAAAAAAARQnAQ',
{
parser: {
type: 'image'
}
}
)
.color('#2E8AE6')
.shape('fill')
.style({
globelOtions: {
ambientRatio: 0.6, // 环境光
diffuseRatio: 0.4, // 漫反射
specularRatio: 0.1 // 高光反射
}
})
.animate(true);

const atomLayer = new EarthLayer()
.color('#2E8AE6')
.shape('atomSphere')
.style({
opacity: 1
});

const bloomLayer = new EarthLayer().color('#fff').shape('bloomSphere')
.style({
opacity: 0.7
});

scene.on('loaded', () => {
scene.addLayer(earthlayer);

scene.addLayer(atomLayer);
scene.addLayer(bloomLayer);

scene.addLayer(flyLine);

earthlayer.setEarthTime(4.0);
});

在地球模式下使用点图层无需做额外的操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
import { CMap, PointLayer, EarthLayer, Earth } from 'cci-map'
// import { CMap, EarthLayer, Earth } from 'cci-map'
// import { PointLayer } from 'cci-layers'
const scene = new CMap({
id: 'map',
map: new Earth({})
});

const d = [
{ lng: 121.61865234375, lat: 25.29437116258816 },
{ lng: 121.058349609375, lat: 25.015928763367857 },
{ lng: 120.7177734375, lat: 24.587090339209634 },
{ lng: 120.28930664062499, lat: 23.936054914599815 },
{ lng: 120.12451171875, lat: 23.553916518321625 },
{ lng: 120.08056640625, lat: 23.120153621695614 },
{ lng: 120.234375, lat: 22.867317960075614 },
{ lng: 120.43212890625, lat: 22.52270570348246 },
{ lng: 120.65185546875, lat: 22.370396344320053 },
{ lng: 120.750732421875, lat: 21.922663209325922 },
{ lng: 120.948486328125, lat: 22.268764039073968 },
{ lng: 121.124267578125, lat: 22.806567100271522 },
{ lng: 121.56372070312499, lat: 23.915970370510227 },
{ lng: 121.88232421875, lat: 24.557116164309626 },
{ lng: 121.95922851562501, lat: 25.075648445630527 },
{ lng: 109.97314453125, lat: 20.076570104545173 },
{ lng: 108.896484375, lat: 19.663280219987662 },
{ lng: 108.61083984375, lat: 18.979025953255267 },
{ lng: 108.80859375, lat: 18.47960905583197 },
{ lng: 109.599609375, lat: 18.35452552912664 },
{ lng: 110.32470703125, lat: 18.771115062337024 },
{ lng: 111.005859375, lat: 19.78738018198621 },
{ lng: 127.657407, lat: 49.76027 },
{ lng: 129.397818, lat: 49.4406 },
{ lng: 130.582293, lat: 48.729687 },
{ lng: 130.987282, lat: 47.790132 },
{ lng: 132.506672, lat: 47.78897 },
{ lng: 133.373596, lat: 48.183442 },
{ lng: 135.026311, lat: 48.47823 },
{ lng: 134.500814, lat: 47.57844 },
{ lng: 134.112362, lat: 47.212467 },
{ lng: 133.769644, lat: 46.116927 },
{ lng: 133.097127, lat: 45.144066 },
{ lng: 131.883454, lat: 45.321162 },
{ lng: 131.025212, lat: 44.967953 },
{ lng: 131.288555, lat: 44.11152 },
{ lng: 131.144688, lat: 42.92999 },
{ lng: 130.633866, lat: 42.903015 },
{ lng: 130.640016, lat: 42.395009 },
{ lng: 129.994267, lat: 42.985387 },
{ lng: 129.596669, lat: 42.424982 },
{ lng: 128.052215, lat: 41.994285 },
{ lng: 128.208433, lat: 41.466772 },
{ lng: 127.343783, lat: 41.503152 },
{ lng: 126.869083, lat: 41.816569 },
{ lng: 126.182045, lat: 41.107336 },
{ lng: 125.079942, lat: 40.569824 },
{ lng: 124.265625, lat: 39.928493 },
{ lng: 122.86757, lat: 39.637788 },
{ lng: 122.131388, lat: 39.170452 },
{ lng: 121.054554, lat: 38.897471 },
{ lng: 121.585995, lat: 39.360854 },
{ lng: 121.376757, lat: 39.750261 },
{ lng: 122.168595, lat: 40.422443 },
{ lng: 121.640359, lat: 40.94639 },
{ lng: 120.768629, lat: 40.593388 },
{ lng: 119.639602, lat: 39.898056 },
{ lng: 119.023464, lat: 39.252333 },
{ lng: 118.042749, lat: 39.204274 },
{ lng: 117.532702, lat: 38.737636 },
{ lng: 118.059699, lat: 38.061476 },
{ lng: 118.87815, lat: 37.897325 },
{ lng: 118.911636, lat: 37.448464 },
{ lng: 119.702802, lat: 37.156389 },
{ lng: 120.823457, lat: 37.870428 },
{ lng: 121.711259, lat: 37.481123 },
{ lng: 122.357937, lat: 37.454484 },
{ lng: 122.519995, lat: 36.930614 },
{ lng: 121.104164, lat: 36.651329 },
{ lng: 120.637009, lat: 36.11144 },
{ lng: 119.664562, lat: 35.609791 },
{ lng: 119.151208, lat: 34.909859 },
{ lng: 120.227525, lat: 34.360332 },
{ lng: 120.620369, lat: 33.376723 },
{ lng: 121.229014, lat: 32.460319 },
{ lng: 121.908146, lat: 31.692174 },
{ lng: 121.891919, lat: 30.949352 },
{ lng: 121.264257, lat: 30.676267 },
{ lng: 121.503519, lat: 30.142915 },
{ lng: 122.092114, lat: 29.83252 },
{ lng: 121.938428, lat: 29.018022 },
{ lng: 121.684439, lat: 28.225513 },
{ lng: 121.125661, lat: 28.135673 },
{ lng: 120.395473, lat: 27.053207 },
{ lng: 119.585497, lat: 25.740781 },
{ lng: 118.656871, lat: 24.547391 },
{ lng: 117.281606, lat: 23.624501 },
{ lng: 115.890735, lat: 22.782873 },
{ lng: 114.763827, lat: 22.668074 },
{ lng: 114.152547, lat: 22.22376 },
{ lng: 113.80678, lat: 22.54834 },
{ lng: 113.241078, lat: 22.051367 },
{ lng: 111.843592, lat: 21.550494 },
{ lng: 110.785466, lat: 21.397144 },
{ lng: 110.444039, lat: 20.341033 },
{ lng: 109.889861, lat: 20.282457 },
{ lng: 109.627655, lat: 21.008227 },
{ lng: 109.864488, lat: 21.395051 },
{ lng: 108.522813, lat: 21.715212 },
{ lng: 108.05018, lat: 21.55238 },
{ lng: 107.04342, lat: 21.811899 },
{ lng: 106.567273, lat: 22.218205 },
{ lng: 106.725403, lat: 22.794268 },
{ lng: 105.811247, lat: 22.976892 },
{ lng: 105.329209, lat: 23.352063 },
{ lng: 104.476858, lat: 22.81915 },
{ lng: 103.504515, lat: 22.703757 },
{ lng: 102.706992, lat: 22.708795 },
{ lng: 102.170436, lat: 22.464753 },
{ lng: 101.652018, lat: 22.318199 },
{ lng: 101.80312, lat: 21.174367 },
{ lng: 101.270026, lat: 21.201652 },
{ lng: 101.180005, lat: 21.436573 },
{ lng: 101.150033, lat: 21.849984 },
{ lng: 100.416538, lat: 21.558839 },
{ lng: 99.983489, lat: 21.742937 },
{ lng: 99.240899, lat: 22.118314 },
{ lng: 99.531992, lat: 22.949039 },
{ lng: 98.898749, lat: 23.142722 },
{ lng: 98.660262, lat: 24.063286 },
{ lng: 97.60472, lat: 23.897405 },
{ lng: 97.724609, lat: 25.083637 },
{ lng: 98.671838, lat: 25.918703 },
{ lng: 98.712094, lat: 26.743536 },
{ lng: 98.68269, lat: 27.508812 },
{ lng: 98.246231, lat: 27.747221 },
{ lng: 97.911988, lat: 28.335945 },
{ lng: 97.327114, lat: 28.261583 },
{ lng: 96.248833, lat: 28.411031 },
{ lng: 96.586591, lat: 28.83098 },
{ lng: 96.117679, lat: 29.452802 },
{ lng: 95.404802, lat: 29.031717 },
{ lng: 94.56599, lat: 29.277438 },
{ lng: 93.413348, lat: 28.640629 },
{ lng: 92.503119, lat: 27.896876 },
{ lng: 91.696657, lat: 27.771742 },
{ lng: 91.258854, lat: 28.040614 },
{ lng: 90.730514, lat: 28.064954 },
{ lng: 90.015829, lat: 28.296439 },
{ lng: 89.47581, lat: 28.042759 },
{ lng: 88.814248, lat: 27.299316 },
{ lng: 88.730326, lat: 28.086865 },
{ lng: 88.120441, lat: 27.876542 },
{ lng: 86.954517, lat: 27.974262 },
{ lng: 85.82332, lat: 28.203576 },
{ lng: 85.011638, lat: 28.642774 },
{ lng: 84.23458, lat: 28.839894 },
{ lng: 83.898993, lat: 29.320226 },
{ lng: 83.337115, lat: 29.463732 },
{ lng: 82.327513, lat: 30.115268 },
{ lng: 81.525804, lat: 30.422717 },
{ lng: 81.111256, lat: 30.183481 },
{ lng: 79.721367, lat: 30.882715 },
{ lng: 78.738894, lat: 31.515906 },
{ lng: 78.458446, lat: 32.618164 },
{ lng: 79.176129, lat: 32.48378 },
{ lng: 79.208892, lat: 32.994395 },
{ lng: 78.811086, lat: 33.506198 },
{ lng: 78.912269, lat: 34.321936 },
{ lng: 77.837451, lat: 35.49401 },
{ lng: 76.192848, lat: 35.898403 },
{ lng: 75.896897, lat: 36.666806 },
{ lng: 75.158028, lat: 37.133031 },
{ lng: 74.980002, lat: 37.41999 },
{ lng: 74.829986, lat: 37.990007 },
{ lng: 74.864816, lat: 38.378846 },
{ lng: 74.257514, lat: 38.606507 },
{ lng: 73.928852, lat: 38.505815 },
{ lng: 73.675379, lat: 39.431237 },
{ lng: 73.960013, lat: 39.660008 },
{ lng: 73.822244, lat: 39.893973 },
{ lng: 74.776862, lat: 40.366425 },
{ lng: 75.467828, lat: 40.562072 },
{ lng: 76.526368, lat: 40.427946 },
{ lng: 76.904484, lat: 41.066486 },
{ lng: 78.187197, lat: 41.185316 },
{ lng: 78.543661, lat: 41.582243 },
{ lng: 80.11943, lat: 42.123941 },
{ lng: 80.25999, lat: 42.349999 },
{ lng: 80.18015, lat: 42.920068 },
{ lng: 80.866206, lat: 43.180362 },
{ lng: 79.966106, lat: 44.917517 },
{ lng: 81.947071, lat: 45.317027 },
{ lng: 82.458926, lat: 45.53965 },
{ lng: 83.180484, lat: 47.330031 },
{ lng: 85.16429, lat: 47.000956 },
{ lng: 85.720484, lat: 47.452969 },
{ lng: 85.768233, lat: 48.455751 },
{ lng: 86.598776, lat: 48.549182 },
{ lng: 87.35997, lat: 49.214981 },
{ lng: 87.751264, lat: 49.297198 },
{ lng: 88.013832, lat: 48.599463 },
{ lng: 88.854298, lat: 48.069082 },
{ lng: 90.280826, lat: 47.693549 },
{ lng: 90.970809, lat: 46.888146 },
{ lng: 90.585768, lat: 45.719716 },
{ lng: 90.94554, lat: 45.286073 },
{ lng: 92.133891, lat: 45.115076 },
{ lng: 93.480734, lat: 44.975472 },
{ lng: 94.688929, lat: 44.352332 },
{ lng: 95.306875, lat: 44.241331 },
{ lng: 95.762455, lat: 43.319449 },
{ lng: 96.349396, lat: 42.725635 },
{ lng: 97.451757, lat: 42.74889 },
{ lng: 99.515817, lat: 42.524691 },
{ lng: 100.845866, lat: 42.663804 },
{ lng: 101.83304, lat: 42.514873 },
{ lng: 103.312278, lat: 41.907468 },
{ lng: 104.522282, lat: 41.908347 },
{ lng: 104.964994, lat: 41.59741 },
{ lng: 106.129316, lat: 42.134328 },
{ lng: 107.744773, lat: 42.481516 },
{ lng: 109.243596, lat: 42.519446 },
{ lng: 110.412103, lat: 42.871234 },
{ lng: 111.129682, lat: 43.406834 },
{ lng: 111.829588, lat: 43.743118 },
{ lng: 111.667737, lat: 44.073176 },
{ lng: 111.348377, lat: 44.457442 },
{ lng: 111.873306, lat: 45.102079 },
{ lng: 112.436062, lat: 45.011646 },
{ lng: 113.463907, lat: 44.808893 },
{ lng: 114.460332, lat: 45.339817 },
{ lng: 115.985096, lat: 45.727235 },
{ lng: 116.717868, lat: 46.388202 },
{ lng: 117.421701, lat: 46.672733 },
{ lng: 118.874326, lat: 46.805412 },
{ lng: 119.66327, lat: 46.69268 },
{ lng: 119.772824, lat: 47.048059 },
{ lng: 118.866574, lat: 47.74706 },
{ lng: 118.064143, lat: 48.06673 },
{ lng: 117.295507, lat: 47.697709 },
{ lng: 116.308953, lat: 47.85341 },
{ lng: 115.742837, lat: 47.726545 },
{ lng: 115.485282, lat: 48.135383 },
{ lng: 116.191802, lat: 49.134598 },
{ lng: 116.678801, lat: 49.888531 },
{ lng: 117.879244, lat: 49.510983 },
{ lng: 119.288461, lat: 50.142883 },
{ lng: 119.279366, lat: 50.582908 },
{ lng: 120.18205, lat: 51.643566 },
{ lng: 120.738191, lat: 51.964115 },
{ lng: 120.725789, lat: 52.516226 },
{ lng: 120.177089, lat: 52.753886 },
{ lng: 121.003085, lat: 53.251401 },
{ lng: 122.245748, lat: 53.431726 },
{ lng: 123.571507, lat: 53.458804 },
{ lng: 125.068211, lat: 53.161045 },
{ lng: 125.946349, lat: 52.792799 },
{ lng: 126.564399, lat: 51.784255 },
{ lng: 126.939157, lat: 51.353894 },
{ lng: 127.287456, lat: 50.739797 },
{ lng: 127.657407, lat: 49.76027 }
];

const pointlayer = new PointLayer()
.source(
d,
{
parser: {
type: 'json',
x: 'lng',
y: 'lat'
}
}
)
.shape('cylinder')
.color('#f00')
.size('', () => [ 1, 1, 10 ])
.active(true);

const earthlayer = new EarthLayer()
.source(
'https://gw.alipayobjects.com/mdn/rms_23a451/afts/img/A*3-3NSpqRqUoAAAAAAAAAAAAAARQnAQ',
{
parser: {
type: 'image'
}
}
)
.shape('fill')
.style({
opacity: 1.0,
radius: 40,
globelOtions: {
ambientRatio: 0.6, // 环境光
diffuseRatio: 0.4, // 漫反射
specularRatio: 0.1 // 高光反射
}
})
.animate(true);

const atomLayer = new EarthLayer()
.color('#2E8AE6')
.shape('atomSphere')
.style({
opacity: 1
});

const bloomLayer = new EarthLayer().color('#fff').shape('bloomSphere')
.style({
opacity: 0.6
});

scene.on('loaded', () => {
scene.addLayer(earthlayer);
scene.addLayer(pointlayer);

scene.addLayer(atomLayer);
scene.addLayer(bloomLayer);

earthlayer.setEarthTime(4.0);
})

3. 数据

3.1 概述

source 地理数据处理模块,主要包含数据解析(parser),和数据处理(transform)

  • data
  • option
    • cluster boolean 是否聚合
    • clusterOption 聚合配置项
    • parser 数据解析配置
    • transforms 数据处理配置
3.1.1 parser

不同数据类型处理成统一数据格式。矢量数据包括 GeoJON, CSV,Json 等不同数据格式,栅格数据,包括 Raster,Image 数据。将来还会支持瓦片格式数据。

空间数据分矢量数据和栅格数据两大类

  • 矢量数据 支持 csv,geojson,json 三种数据类型
  • 栅格数据 支持 image,Raster
3.1.2 transform

数据转换,数据统计,网格布局,数据聚合等数据操作。

3.2 API

cluster boolean 可选 可以只设置

clusterOption 可选

  • radius 聚合半径 number default 40
  • minZoom: 最小聚合缩放等级 number default 0
  • maxZoom: 最大聚合缩放等级 number default 16

parser 配置项

  • type: csv|json|geojson|image|raster
  • 其他可选配置项,具体和数据格式相关

geojson 数据为默认数据格式,可以 不设置 parser 参数

1
layer.source(data);

Source 更新

如果数据发生改变,可以需要更新数据 可以通过调用 layer 的 setData 方法实现数据的更新

1
layer.setData(data);

getClustersLeaves(cluster_id)

聚合图使用,获取聚合节点的原始数据

参数: id 聚合节点的 cluster_id

1
2
3
layer.on('click', (e) => {
console.log(source.getClustersLeaves(e.feature.cluster_id));
});

getClustersLeaves(cluster_id)

聚合图使用,获取聚合节点的原始数据

参数: id 聚合节点的 cluster_id

1
2
3
layer.on('click', (e) => {
console.log(source.getClustersLeaves(e.feature.cluster_id));
});

3.3 transforms

tranforms 处理的是的标准化之后的数据 标准化之后的数据结构包括 coordinates 地理坐标字段,以及其他属性字段。

处理完之后返回的也是标准数据

1
2
3
4
5
6
7
8
9
[
{
"coordinates": [[]], // 地理坐标字段
"_id": "122", // 标准化之后新增字段
"name": "test",
"value": 1
// ....
}
]

目前支持两种热力图使用的数据处理方法 grid,hexagon transform 配置项

  • type 数据处理类型
  • tansform cfg 数据处理配置项
3.3.1 grid

生成方格网布局,根据数据字段统计,主要在网格热力图中使用

  • type: ‘grid’,
  • size: 网格半径
  • field: 数据统计字段
  • method: 聚合方法 count,max,min,sum,mean 5 个统计维度
1
2
3
4
5
6
7
8
9
10
layer.source(data, {
transforms: [
{
type: 'grid',
size: 15000,
field: 'v',
method: 'sum',
},
],
});
3.3.2 hexagon

生成六边形网格布局,根据数据字段统计

  • type: ‘hexagon’,
  • size: 网格半径
  • field: 数据统计字段
  • method:聚合方法 count,max,min,sum,mean 5 个统计维度
3.3.3 join

数据连接,业务中跟多情况是地理数据和业务数据分开的两套数据,我们可与通过 join 方法将地理数据和业务数据进行关联。

配置项

  • type: join
  • sourceField 需要连接的业务数据字段名称
  • data 需要连接的数据源 仅支持 json 格式
  • targetField 关联的地理数据字段名称
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
const data = {
type: 'FeatureCollection',
features: [
{
type: 'Feature',
properties: {
city: '北京',
},
geometry: {},
},
],
};

const data2 = [
{
name: '北京',
value: 13,
},
{
name: '天津',
value: 20,
},
];
// data 是地理数据
// data2 属性数据或者业务数据

// 通过join方法我们就可以将两个数据连接到一起

layer
.source(data, {
transforms: [
{
type: 'join',
sourceField: 'name', //data1 对应字段名
targetField: 'city', // data 对应字段名 绑定到的地理数据
data: data2,
},
],
})
.color('value'); // 可以采用data1的value字段进行数据到颜色的映射

3.4 GeoJSON

GeoJSON 是一种对各种地理数据结构进行编码的格式。GeoJSON 对象可以表示几何、特征或者特征集合。GeoJSON 支持下面几何类型:点、线、面、多点、多线、多面和几何集合。GeoJSON 里的特征包含一个几何对象和其他属性,特征集合表示一系列特征。 The GeoJSON Format。

source 支持 传入 Geometry 集合 FeatureCollection

3.4.1 Feature Collection Object

一个 feature Colletion 由对个 feature 组成

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": {
"name": "tom"
},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[-2.8125, 34.59704151614417],
[65.390625, 34.59704151614417],
[65.390625, 61.10078883158897],
[-2.8125, 61.10078883158897],
[-2.8125, 34.59704151614417]
]
]
}
}
]
}
3.4.2 Feature Object

一个 feature 有 geometry 空间信息,properties 属性信息,其中 geometry 是必须字段

1
2
3
4
5
{
"type": "Feature",
"properties": {},
"geometry": {}
}
3.4.3 Gemetry Object

支持 Gemetry Object 类型

  • Point
1
2
3
4
{
"type": "Point",
"coordinates": [100.0, 0.0]
}
  • MultiPoint
1
2
3
4
5
6
7
{
"type": "MultiPoint",
"coordinates": [
[100.0, 0.0],
[101.0, 1.0]
]
}
  • LineSring
1
2
3
4
5
6
7
{
"type": "LineString",
"coordinates": [
[100.0, 0.0],
[101.0, 1.0]
]
}
  • MultiLineString
1
2
3
4
5
6
7
8
9
10
11
12
13
{
"type": "MultiLineString",
"coordinates": [
[
[100.0, 0.0],
[101.0, 1.0]
],
[
[102.0, 2.0],
[103.0, 3.0]
]
]
}
  • Polygon
1
2
3
4
5
6
7
8
9
10
11
12
{
"type": "Polygon",
"coordinates": [
[
[100.0, 0.0],
[101.0, 0.0],
[101.0, 1.0],
[100.0, 1.0],
[100.0, 0.0]
]
]
}
  • Polygon With holes:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
{
"type": "Polygon",
"coordinates": [
[
[-170.0, 10.0],
[170.0, 10.0],
[170.0, -10.0],
[-170.0, -10.0],
[-170.0, 10.0]
],
[
[175.0, 5.0],
[-175.0, 5.0],
[-175.0, -5.0],
[175.0, -5.0],
[175.0, 5.0]
]
]
}
  • MultiPolygon
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
{
"type": "MultiPolygon",
"coordinates": [
[
[
[102.0, 2.0],
[103.0, 2.0],
[103.0, 3.0],
[102.0, 3.0],
[102.0, 2.0]
]
],
[
[
[100.0, 0.0],
[101.0, 0.0],
[101.0, 1.0],
[100.0, 1.0],
[100.0, 0.0]
],
[
[100.2, 0.2],
[100.8, 0.2],
[100.8, 0.8],
[100.2, 0.8],
[100.2, 0.2]
]
]
]
}

geojson 详细文档

3.4.4 Geojson 相关的 JS 库
  • 地理统计分析工具

turfjs: 地理数据计算,处理,统计,分析的 Javascript 库

  • 在线工具:

http://geojson.io/ 可以在线查看,绘制,修改 GeoJSON 数据

https://mapshaper.org/ 可以查看较大的 geojson,还能够简化 GeoJSON 数据

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

3.5 JSON

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

⚠️ json 不是标准的地理数据结构,因此在使用时务必要设置 Parser。Parser 支持两种解析方式

3.5.1 简易解析方式

该方式只支持解析的点数据,或者只有两个点的线段,或者弧线数据

  • type string 必选 json
  • x string 点数据表示 经度
  • y string 点数据表示 纬度
  • x1 string 经度
  • x2 string 纬度

如果数据是点数据,只需要设置 x,y 字段即可

如果是线段,弧线数据,需要知道起止点坐标既,x,y,x1,y1

1
2
3
4
5
6
7
layer.source(data, {
parser: {
type: 'json',
x: 'lng',
y: 'lat',
},
});
3.5.2 通用解析方式

可也解析任意复杂的点,线面

  • type string 必选 json
  • coordinates array 必选,主要用于表达比较复杂的格式,等同于 geojson coordinates 属性
1
2
3
4
5
6
layer.source(data, {
parser: {
type: 'json',
coordinates: 'coord',
},
});
3.5.3 点数据实例

简易解析

  • type json
  • x: 经度字段
  • y: 纬度字段
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
const data = [
{
lng: 112.345,
lat: 30.455,
value: 10,
},
{
lng: 114.345,
lat: 31.455,
value: 10,
},
];

layer.source(data, {
parser: {
type: 'json',
x: 'lng',
y: 'lat',
},
});

通用解析

点 coodinates 数据格式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const data = [
{
coord: [112.345, 30.455],
value: 10,
},
{
coord: [114.345, 32.455],
value: 10,
},
];

layer.source(data, {
parser: {
type: 'json',
coordinates: 'coord',
},
});
3.5.4 线数据示例

简易解析

  • type: json
  • x string 经度
  • y string 纬度
  • x1 string 经度
  • x2 string 纬度

简易解析只支持两个点组成的线段,主要再绘制弧线的时候比较常用,只需指定线段的起止点坐标

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
const data = [
{
lng1: 112.345,
lat1: 30.455,
lng2: 112.345,
lat2: 30.455,
value: 10,
},
{
lng1: 114.345,
lat1: 31.455,
lng2: 112.345,
lat2: 30.455,
value: 10,
},
];

layer.source(data, {
parser: {
type: 'json',
x: 'lng1',
y: 'lat1',
x1: 'lng1',
y1: 'lat2',
},
});

通用解析

绘制线段、弧线也支持使用 coordinates 组织数据

coordinates 包含两个坐标, 第一个坐标 对应 x, y 第二个坐标 对应 x1, y1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const data = [
{
id: '1',
coord: [
[101.953125, 50.51342652633956],
[119.17968749999999, 33.137551192346145],
],
},
];
layer.source(data, {
parser: {
type: 'json',
coordinates: 'coord',
},
});

如果需要使用绘制轨迹数据,需要通过 coodinates 指定线的点序列。

coordinate 格式 geojson 的 coordinate 字段 支持 LineString, MultiLineString

线 coodinates 数据格式

1
2
3
4
5
6
7
8
9
10
const data = {
name: 'path1',
path: [
[58.00781249999999, 32.84267363195431],
[85.78125, 25.16517336866393],
[101.953125, 41.77131167976407],
[114.9609375, 39.639537564366684],
[117.42187500000001, 28.613459424004414],
],
};

使用时通过 coordinates 指定

1
2
3
4
5
6
layer.source(data, {
parser: {
type: 'json',
coordinates: 'path',
},
});
3.5.5 面数据示例

面数据 coordinates 字段比较复杂不支持简易的解析方式

通用解析

需要指定 coordinates 字段, 格式同 GeoJSON 的 coordinates 字段

面 coodinates 数据格式

注意面数据 coord 是三层数据结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
[
{
type: 'Polygon',
geometryCoord: [
[
[115.1806640625, 30.637912028341123],
[114.9609375, 29.152161283318915],
[117.79541015625001, 27.430289738862594],
[118.740234375, 29.420460341013133],
[117.46582031249999, 31.50362930577303],
[115.1806640625, 30.637912028341123],
],
],
},
];

layer.source(data, {
parser: {
type: 'json',
coordinates: 'geometryCoord',
},
});

3.6 CSV

支持 CSV 以逗号分隔的 CSV 数据加载。

CSV 是文本数据结构,很难表达复杂的地理数据结构,因此 CSV 仅支持两种数据结构

  • 点数据 需要指定经度,纬度坐标
  • 线段,弧线数据 需要指定 起止点的 经度,纬度坐标
3.6.1 parser
  • type string 必选 json
  • x string 点数据表示 经度
  • y string 点数据表示 纬度
  • x1 string 经度
  • x2 string 纬度

点数据通过 CSV 加载

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
// layer.source(data, {
// parser: {
// type: 'csv',
// x: 'lng',
// y: 'lat',
// }
// })
import { CMap, PointLayer, GaodeMap } from 'cci-map';
const scene = new CMap({
id: 'map',
map: new GaodeMap({
pitch: 0,
style: 'dark',
center: [ 112, 23.69 ],
zoom: 2.5
})
});
scene.on('loaded', () => {
fetch(
'https://gw.alipayobjects.com/os/basement_prod/9078fd36-ce8d-4ee2-91bc-605db8315fdf.csv'
)
.then(res => res.text())
.then(data => {
const pointLayer = new PointLayer({})
.source(data, {
parser: {
type: 'csv',
x: 'Longitude',
y: 'Latitude'
}
})
.shape('circle')
.active(true)
.animate(true)
.size(56)
.color('#4cfd47')
.style({
opacity: 1
});

scene.addLayer(pointLayer);
});
});

线段弧线数据通过 CSV 加载

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
// layer.source(data, {
// parser: {
// type: 'csv',
// x: 'lng1',
// y: 'lat1',
// x1: 'lng1',
// y1: 'lat2',
// },
// })
import { CMap, LineLayer, Mapbox } from 'cci-map';
const scene = new CMap({
id: 'map',
map: new Mapbox({
style: 'dark',
pitch: 0,
center: [ 107.77791556935472, 35.443286920228644 ],
zoom: 2.9142882493605033
})
});
scene.on('loaded', () => {
fetch('https://gw.alipayobjects.com/os/rmsportal/UEXQMifxtkQlYfChpPwT.txt')
.then(res => res.text())
.then(data => {
const layer = new LineLayer({})
.source(data, {
parser: {
type: 'csv',
x: 'lng1',
y: 'lat1',
x1: 'lng2',
y1: 'lat2'
}
})
.size(1)
.shape('arc')
.color('#8C1EB2')
.style({
opacity: 0.8,
blur: 0.99
});
scene.addLayer(layer);
});
});

3.7 Image

Image 数据主要用于在地图根据经纬度范围添加图图片,比如一幅纸制地图扫描版你要放在地图显示。

3.7.1 parser
  • type: image
  • extent: 图像的经纬度范围 [minlng, minlat,maxLng, maxLat]

根据图片的经纬度范围,将图片添加到地图上。

1
2
3
4
5
6
7
8
9
layer.source(
'https://gw.alipayobjects.com/zos/rmsportal/FnHFeFklTzKDdUESRNDv.jpg',
{
parser: {
type: 'image',
extent: [121.168, 30.2828, 121.384, 30.4219],
},
},
);

3.8 栅格

Raster 图层主要实现栅格数据的可视化,栅格数据主要来源是卫星遥感数据,如数字高程图,植被分布图,夜光图。

3.8.1 parser
  • type: raster
  • extent: 栅格的经纬度范围 [minlng, minlat,maxLng, maxLat]
  • width 数据宽度
  • height 数据高度

根据图片的经纬度范围,将图片添加到地图上。

1
2
3
4
5
6
7
8
9
layer.source(
'https://gw.alipayobjects.com/zos/rmsportal/FnHFeFklTzKDdUESRNDv.jpg',
{
parser: {
type: 'raster',
extent: [121.168, 30.2828, 121.384, 30.4219],
},
},
);

4 组件

4.1 Control 控件

地图组件用于控制地图的状态如果平移,缩放,或者展示地图一些的辅助信息如图例、比例尺。

目前支持 Control

  • Zoom 放大缩小
  • Scale 比例尺
  • Layers 图层列表
4.1.1 构造函数 option

position: string 控件位置支持 8 个方位

  • bottomright
  • topright
  • bottomleft
  • topleft
  • topcenter
  • bottomcenter
  • leftcenter
  • rightcenter
4.1.2 Zoom 组件

放大缩小组件 默认左上角

1
2
3
4
5
6
import { Zoom } from 'cci-map';
const zoomControl = new Zoom({
position: 'topleft',
});

scene.addControl(zoomControl);
4.1.3 Scale 组件

比例尺组件 默认左下角

1
2
3
4
5
6
import { Scale } from 'cci-map';
const scaleControl = new Scale({
position: 'bottomleft',
});

scene.addControl(scaleControl);
4.1.4 Layers

图层列表目前支持可视化的图层控制

option 控件配置项 overlayers 将一组图层添加到图层列表, overlayers Object key: 列表显示的图层名字可以自定义 layer: 图层对象

1
2
3
4
5
6
7
8
9
10
11
12
import { Layers } from 'cci-map'
const layer = {
图层一: layer1,
图层二: layer2,
};
const overlayers = {
点图层: layer,
};
const layersControl = new Layers({
overlayers,
});
scene.addControl(layersControl);

4.2 Popup 信息框

地图标注信息窗口,用于展示地图要素的属性信息

4.2.1 构造函数 option
  • closeButton 是否显示关闭按钮,布尔值,默认为 true。
  • closeButtonOffsets 显示关闭按钮时生效,[number, number],默认为 [0, 0],以右上角为起始点。
  • closeOnClick 是否在点击地图的时候关闭弹框,布尔值,默认为 true
  • maxWidth 弹框最宽值,默认为 240px
  • anchor 弹框锚点,默认值为 bottom,可选值有 center、top、top-left、left、bottom-left、bottom、bottom-right、right、top-right
4.2.2 方法
  • setLnglat 设置 popup 的经纬度位置

参数:lnglat 支持数组

1
[112, 32];

经纬度对象

1
2
3
4
5
const lnglat = {
lng: 112.323,
lat: 30.456,
};
popup.setLnglat([112, 32]);
  • setHTML 设置 popup html 内容

参数:html 字符串

1
2
3
4
5
var html = `<p>省份
${feature.s} </p><p>地区
${feature.m}</p><p>数值
${feature.t}</p>`;
popup.setHTML(html);
  • setDOMContent

参数:htmlNode dom 对象 区别于 setHtml 对象只能传字符串

  • setText

设置 popup 显示文本内容

1
popup.setText('hello world');
  • setMaxWidth

设置 popup 最大宽度

1
popup.setMaxWidth('300px');
  • open

显示 popup

1
popup.open();
  • close

关闭 popup

1
popup.close();
  • remove

移除 popup

1
popup.remove();
4.2.3 事件
  • open
1
popup.on('open', () => {});
  • close
1
popup.on('close', () => {});
4.2.4 示例代码

添加 popup

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import { CMap, Popup, Mapbox } from 'cci-map'
// import { CMap, Mapbox } from 'cci-map'
// import { Popup } from 'cci-layers'
const scene = new CMap({
id: 'map',
map: new Mapbox({
style: 'light',
pitch: 0,
center: [ 121.4316962, 31.26082325 ],
zoom: 12.056
})
});
scene.on('loaded', () => {
var html = '<p>测试</p>';
const popup = new Popup({
offsets: [ 0, 20 ]
}).setLnglat([112, 32]).setHTML(html);
scene.addPopup(popup)
})

通过组件实例化 popup

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import Vue from 'vue'
import { CMap, Popup, Mapbox } from 'cci-map';
// import { CMap, Mapbox } from 'cci-map'
// import { Popup } from 'cci-layers'

const comp = {
render() {
return <div>测试</div>
}
} // 编写 vue 单页面组件

const VuePopup = Vue.extend(comp)

const scene = new CMap({
id: 'map',
map: new Mapbox({
style: 'light',
pitch: 0,
center: [ 121.4316962, 31.26082325 ],
zoom: 12.056
})
});
scene.on('loaded', () => {
const popup = new Popup({
offsets: [ 0, 20 ]
}).setLnglat([112, 32])
.setHTML(VuePopup.$mount().$el) // 可传入 dom,推荐
// .setHTML(VuePopup.$mount().$el.outerHTML) // 可传入 html 字符串
scene.addPopup(popup)
})

4.3 Marker 标注

Marker 地图标注 目前只支持 2D Dom 标注

1
2
const Marker = new CciMap.Marker(option)
scene.addMarker(marker)
4.3.1 构造函数 option
  • color string map-marker.png 设置默认 marker 的颜色
  • element Dom|string 自定义 marker Dom 节点,可以是 dom 实例,也可以是 dom id
  • anchor string 锚点位置 支持 center, top, top-left, top-right, bottom, bottom-left,bottom-right,left, right
  • offsets Array 偏移量 [ 0, 0 ] 分别表示 X, Y 的偏移量
  • extData 用户自定义属性,支持任意数据类型,存储 marker 属性信息
4.3.2 方法
  • setLnglat

设置 marker 经纬度位置

  • remove

移除 marker

  • getElement

获取 marker dom Element

  • setElement 设置 element 通过此方法更新 Marker 样式

参数:element dom

  • getLngLat

获取 marker 经纬度坐标

  • togglePopup

开启或者关闭 marker 弹出框

  • openPopup

打开 Popup

  • closePopup

关闭 popup

  • setPopup

为 marker 设置 popup

  • getPopup

获取 marker 弹出框

  • getExtData

获取用户自定义数据

  • setExtData(data)

设置用户自定义数据

4.3.3 事件

鼠标事件

  • mousemove
  • click
  • mousedown
  • mouseup
  • dblclick
  • contextmenu
  • mouseover
  • mouseout

事件返回数据

  • target 事件触发源
  • data extData 用户自定义数据
  • lnglat marker 经纬度
1
marker.on('click', (e) => {});
4.3.4 示例代码

默认 Marker

1
const marker = new CciMap.Marker({color:'blue'})

自定义 Marker

1
2
3
4
5
6
7
8
9
10
var el = document.createElement('label');
el.className = 'labelclass';
el.textContent = data[i].v;
el.style.background = getColor(data[i].v);

const marker = new CciMap.Marker({
element: el,
}).setLnglat([data[i].x, data[i].y]);

scene.addMarker(marker);

设置 popup

1
2
3
4
5
6
7
8
9
var popup = new CciMap.Popup({
anchor: 'left',
}).setText(item.name);

new CciMap.Marker({
element: el,
})
.setLnglat(item.coordinates)
.setPopup(popup);

4.4 Marker 图层

MarkerLayer 是 Marker 的升级版,Marker 是独立的地图标注,MarkerLayer 则是统一管理大量的 Marker 数据。

技术差异

  • Marker Dom 绘制一个地图元素
  • MarkerLayer 统一管理多个 DomMarker
  • PointLayer 通过 WebGL 绘制元素。

功能差异

  • MarkerLayer 元素的自定义性比较强,任何 HTML+ CSS 的组合都可以绘制在地图上。
  • PointLayer 自定义性比较弱,实现成本比较高,优势可以绘制大量的数据,性能比较好。
1
2
3
4
5
6
import { Marker, MarkerLayer } from 'cci-map';
const markerLayer = new MarkerLayer(option);

// 调用 addMarker方法 将多个Marker添加到Layer

scene.addMarkerLayer(markerLayer);
4.4.1 构造函数 option
  • cluster 聚合 boolean 默认 false
  • clusterOption 聚合配置
    • field string 聚合统计字段
    • method sum| max| min| mean
    • element function 通过回调函数设置聚合 Marker 的样式,返回 dom 元素 回调函数包含以下参数
    • point_count 默认 聚合元素个数
    • clusterData Array 聚合节点的原始数据
    • point_sum 聚合求和 根据 field 和 method 计算
    • point_max 聚合最大值 根据 field 和 method 计算
    • point_min 聚合最小值 根据 field 和 method 计算
    • point_mean 聚合平均值 根据 field 和 method 计算
4.4.2 方法
  • addMarker

参数: marker IMarker 需要添加的 Marker

1
2
3
// 通过 Marker 对象实例化一个 Marker
const marker = new Marker().setLnglat(); // 添加进Marker必须设置经纬度才能添加
markerLayer.addMarker(marker);

为 Marker 添加属性信息, 如果聚合参数设置统计配置项 field| method需要为 Marker 添加属性信息。通过 Marker 的 extData 配置项设置 Marker 属性信息

1
2
3
4
5
6
const marker = new Marker({
extData: nodes.features[i].properties,
}).setLnglat({
lng: coordinates[0],
lat: coordinates[1],
});
  • removeMarker

从 MarkerLayer 移除 Marker

  • getMarkers

获取 MarkerLayer 中的所有 Marker

  • clear

清除掉所有的 Marker

4.4.3 CMap 类方法
  • addMarkerLayer

添加 MarkerLayer

1
scene.addMarkerLayer(layer);
  • removeMarkerLayer

移除 MarkerLayer

1
scene.removeMarkerLayer(layer);
4.4.4 示例

markerLayer

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
import { CMap, Marker, MarkerLayer, GaodeMap } from 'cci-map';

const scene = new CMap({
id: 'map',
map: new GaodeMap({
style: 'light',
center: [ 105.790327, 36.495636 ],
pitch: 0,
zoom: 4
})
});
scene.on('loaded', () => {
addMarkers()
});
function addMarkers() {
fetch(
'https://gw.alipayobjects.com/os/basement_prod/67f47049-8787-45fc-acfe-e19924afe032.json'
)
.then(res => res.json())
.then(nodes => {
const markerLayer = new MarkerLayer();
for (let i = 0; i < nodes.length; i++) {
if (nodes[i].g !== '1' || nodes[i].v === '') {
continue;
}
const el = document.createElement('label');
el.className = 'labelclass';
el.textContent = nodes[i].v + '℃';
el.style.background = getColor(nodes[i].v);
el.style.borderColor = getColor(nodes[i].v);
const marker = new Marker({
element: el
}).setLnglat({ lng: nodes[i].x * 1, lat: nodes[i].y });
markerLayer.addMarker(marker);
}
scene.addMarkerLayer(markerLayer);
});
}

function getColor(v) {
const colors = [ '#ffffe5', '#f7fcb9', '#d9f0a3', '#addd8e', '#78c679', '#41ab5d', '#238443', '#005a32' ];
return v > 50
? colors[7]
: v > 40
? colors[6]
: v > 30
? colors[5]
: v > 20
? colors[4]
: v > 10
? colors[3]
: v > 5
? colors[2]
: v > 0
? colors[1]
: colors[0];
}

markerLayer 聚合

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import { CMap, Marker, MarkerLayer, GaodeMap } from 'cci-map';

const scene = new CMap({
id: 'map',
map: new GaodeMap({
style: 'light',
center: [ 105.790327, 36.495636 ],
pitch: 0,
zoom: 4
})
});
scene.on('loaded', () => {
addMarkers();
scene.render();
});
function addMarkers() {
fetch(
'https://gw.alipayobjects.com/os/basement_prod/d3564b06-670f-46ea-8edb-842f7010a7c6.json'
)
.then(res => res.json())
.then(nodes => {
const markerLayer = new MarkerLayer({
cluster: true
});
for (let i = 0; i < nodes.features.length; i++) {
const { coordinates } = nodes.features[i].geometry;
const marker = new Marker().setLnglat({
lng: coordinates[0],
lat: coordinates[1]
});
markerLayer.addMarker(marker);
}
scene.addMarkerLayer(markerLayer);
});
}

5 图层

5.1 图层基类

Layer 接口设计遵循图形语法,所有图层都继承于该基类。

语法示例

1
2
3
4
5
6
7
8
const layer = new Layer(option)
.source()
.color()
.size()
.shape()
.style();

scene.addLayer(layer);
5.1.1 构造函数配置项
  • name

string optional default: 自动数字编号

设置图层名称,可根据 name 获取 layer;

  • visible

图层是否可见 {bool } default true

  • zIndex

图层绘制顺序,数值大绘制在上层,可以控制图层绘制的上下层级 {int} default 0

  • minZoom

图层显示最小缩放等级,(0-18) {number} Mapbox (0-24) 高德 (2-19)

  • maxZoom

图层显示最大缩放等级 (0-18) {number} Mapbox (0-24) 高德 (2-19)

  • autoFit

layer 初始化完成之后,是否自动缩放到图层范围 {bool } default false

  • pickingBuffer

图层拾取缓存机制,如 1px 宽度的线鼠标很难拾取(点击)到, 通过设置该参数可扩大拾取的范围 {number} default 0

  • blend

图层元素混合效果 (normal 正常效果 默认, additive 叠加模式 subtractive 相减模式 max 最大值)

5.1.2 方法
5.1.2.1 数据方法

source

数据源为 layer 设置数据 source(data,config)

参数

-data {geojson|json|csv} 源数据

  • config 可选 数据源配置项
    • parser 数据解析,默认是解析层 geojson
    • transforms [transform,transform ] 数据处理转换 可设置多个

parser 和 transforms 参见数据配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
layer.source(data, {
parser: {
type: 'csv',
x: 'lng',
y: 'lat',
},
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;
},
},
{
type: 'hexagon',
size: 6000,
field: 'v',
method: 'sum',
},
],
});

scale

设置数据字段映射方法

  • field 字段名。
  • scaleConfig 列定义配置,对象类型,可配置的属性如下:

连续型

  • linear 线性
  • log
  • pow 指数型

连续分类型

  • quantile 等分位
  • quantize 等间距

枚举型

  • cat 枚举
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
layer.scale('name', {
type: 'cat',
});

// 设置多个scale

// 字段名为 key, value 为scale配置项

layer.scale({
name: {
type: 'cat',
},
value: {
type: 'linear',
},
});
5.1.2.2 视觉编码方法

可视化编码是将数据转换为可视形式的过程,目前支持形状(shape),大小(size),颜色(color) 3 种视觉通道,你可以指定数据字段,为不同要素设置不同的图形属性。

size

将数据值映射到图形的大小上的方法,具体 size 的表示具体意义可以查看对应图层的文档

1
2
3
4
5
6
7
8
9
10
pointLayer.size(10); // 常量
pointLayer.size('type'); // 使用字段映射到大小
pointLayer.size('type', [0, 10]); // 使用字段映射到大小,并指定最大值和最小值
pointLayer.size('type', (type) => {
// 回调函数
if (type === 'a') {
return 10;
}
return 5;
});
  • size(value)

传入数字常量,如 pointLayer.size(20)

  • size(field)

根据 field 字段的值映射大小,使用默认的最大值 max:10 和最小值 min: 1。

  • size(field, callback)

使用回调函数控制图形大小。

参数:callback: function 回调函数。

1
2
3
4
5
6
pointLayer.size('age', (value) => {
if (value === 1) {
return 5;
}
return 10;
});

color

将数据值映射到图形的颜色上的方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
layer.color('red'); // 常量颜色
layer.color('type'); // 对 type 字段进行映射,使用内置的颜色
layer.color('type', ['red', 'blue']); // 指定颜色
layer.color('type', (type) => {
// 通过回调函数
if (type === 'a') {
return 'red';
}
return 'blue';
});
layer.color('type*value', (type, value) => {
//多个参数,通过回调函数
if (type === 'a' && value > 100) {
return 'red';
}
return 'blue';
});
  • color(value)

参数:value :string 只支持接收一个参数

value 可以是:映射至颜色属性的数据源字段名,如果数据源中不存在这个字段名的话,则按照常量进行解析,这时会使用默认提供的颜色; 也可直接指定某一个具体的颜色值 color,如 ‘#fff’, ‘white’,’rgba(255,0,0,0.5)’ ,rgb(255,0,1) 等

1
2
layer.color('name'); // 映射数据字段
layer.color('white'); // 指定颜色
  • color(field, colors)

参数:field: stringfield 为映射至颜色属性的数据源字段名,也支持指定多个参数。colors: string | array | function

colors 的参数有以下情况: 如果为空,即未指定颜色的数组,那么使用内置的全局的颜色;如果需要指定颜色,则需要以数组格式传入,那么分类的颜色按照数组中的颜色确定。

1
2
layer.color('name'); // 使用默认的颜色
layer.color('name', ['red', 'blue']); // 使用传入的指定颜色

colors 如果是回调函数,则该回调函数的参数为对应字段的数值,具体使用如下,当 color 映射为多个字段时,参数按照字段声明的顺序传入:

1
2
3
4
5
6
7
8
9
10
11
12
layer.color('gender', (value) => {
if (value === 1) {
return 'red';
}
return 'blue';
});
layer.color('gender*age', (gender, age) => {
if (age === 20 && gender == ' 男') {
return 'red';
}
return 'blue';
});

shape

将数据值映射到图形的形状上的方法。

  • shape(shape)

参数shape string

只支持接收一个参数,指定几何图像对象绘制的形状。下表列出了不同的 图层 几何图形对象支持的 shape 形状

layer 类型 shape 类型 备注
point 2d:point,circle, square, triangle,hexagon,image,text 3d:circle,triangle,hexagon,square
line line,arc, arc3d, greatcircle
polygon fill,line, extrude
  • shape(field, shapes)

  • shape(field, callback)

style

全局设置图形显示属性

  • opacity 设置透明度
  • stroke 线填充颜色 仅点图层支持
  • offsets 偏移量 [number, number] x 和 y 方向偏移 仅支持 pointLayer
  • strokeWidth 线的宽度 仅点图层支持
1
2
3
4
layer.style({
opacity: 0.8,
stroke: 'white',
});
5.1.2.3 通用样式数据纹理映射

从 L7 2.5 开始,各图层样式将逐步支持样式数据映射

layer 类型 支持的样式字段 备注
point/fill opacity、strokeOpacity、strokeWidth、stroke、offsets shape circle、triangle…
point/image opacity、offsets offsets 经纬度偏移
point/normal opacity、offsets
point/text opacity、strokeWidth、stroke、textOffset textOffset 相对文字画布位置的偏移
point/extrude opacity
polygon/fill opacity
polygon/extrude opacity
line/line opacity
line/arc opacity
line/arc3d opacity
line/great_circle opacity
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
import { CMap, PointLayer, GaodeMap } from 'cci-map';
// import { CMap, GaodeMap } from 'cci-map';
// import { PointLayer } from 'cci-layers';
const scene = new CMap({
id: 'map',
map: new GaodeMap({
center: [ 112, 30.267069 ],
pitch: 0,
style: 'dark',
zoom: 6
})
});
scene.on('loaded', () => {
fetch('https://gw.alipayobjects.com/os/bmw-prod/450b2d95-006c-4bad-8269-15729269e142.json')
.then(res => res.json())
.then(data => {
const layer = new PointLayer()
.source(data, {
parser: {
type: 'json',
x: 'lng',
y: 'lat'
}
})
.shape('circle')
.color('color')
.size('value', v => 5 + 15 * v)
.style({
stroke: 'strokeColor',
strokeWidth: 'strokeWidth',
strokeOpacity: [
'strokeOpacity',
d => {
return d * 2;
}
],

opacity: 'opacity'
})
.active(true);
scene.addLayer(layer);
});

});
5.1.2.4 线图层纹理方法

目前在线图层上单独加上了纹理方法的支持

为图层绑定纹理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// 首先在全局加载图片资源
scene.addImage(
'plane',
'https://gw.alipayobjects.com/zos/bmw-prod/0ca1668e-38c2-4010-8568-b57cb33839b9.svg',
);

const layer = new LineLayer({
blend: 'normal',
})
.source(data, {
parser: {
type: 'json',
x: 'lng1',
y: 'lat1',
x1: 'lng2',
y1: 'lat2',
},
})
.size(25)
.shape('arc')
.texture('plane') // 为图层绑定纹理
.color('#8C1EB2')
.style({
lineTexture: true, // 开启线的贴图功能
iconStep: 30, // 设置贴图纹理的间距
textureBlend: 'replace', // 设置纹理混合方式,默认值为 normal,可选值有 normal/replace 两种
});
5.1.2.5 图层更新方法

如果已经添加了图层,需要修改图层显示样式可以再次调用图形映射方法,然后调用 scene.render()更新渲染即可

1
2
3
4
layer.color('blue');
layer.size(10);
layer.style({});
scene.render();

setData

更新 Source 数据

参数:

  • data 数据
  • option 默认和初始配置项一致,如果数据格式相同可不设置

调用 setData 方法会自动更新图层渲染

1
layer.setData(data);

setBlend(type)

设置图层叠加方法 参数:

  • type blend 类型
5.1.2.6 图层控制方法

show

图层显示

1
layer.show();

hide

图层隐藏

1
layer.hide();

isVisible

图层是否可见

return true | false

setIndex

设置图层绘制顺序

fitBounds

缩放到图层范围

1
layer.fitBounds();

setMinZoom

设置图层最小缩放等级

参数

  • zoom {number}
1
layer.setMinZoom(zoom);

setMaxZoom

设置图层最大缩放等级

参数

  • zoom {number}
1
layer.setMaxZoom(zoom)
5.1.2.7 图层交互方法

active

开启或者关闭 mousehover 元素高亮效果

参数: activeOption | boolean

activeOption -color 填充颜色

1
2
3
4
5
6
7
8
9
10
11
// 开启 Active  使用默认高亮颜色
layer.active(true);

// 开启 Active 自定义高亮颜色

layer.active({
color: 'red',
});

// 关闭高亮效果
layer.active(false);

setActive

根据元素 ID 设置指定元素 hover 高亮

1
layer.setActive(id);

select

开启或者关闭 mouseclick 元素选中高亮效果

参数: selectOption | boolean

selectOption -color 填充颜色

1
2
3
4
5
6
7
8
9
10
11
// 开启 Active  使用默认高亮颜色
layer.select(true);

// 开启 Active 自定义高亮颜色

layer.select({
color: 'red',
});

// 关闭高亮效果
layer.select(false);

setSelect

根据元素 ID 设置指定元素 click 选中 高亮

1
layer.setSelect(id);
5.1.2.8 图层框选

boxSelect

参数 option

  • box [x1: number, y1: number, x2: number, y2: number] 相较于
  • cb (…args: any[]) => void 传入的回调方法,返回框选内部的 feature
1
2
3
layer.boxSelect(box, cb);
// (x1, y1), (x2, y2) 框选的方框左上角和右下角相对于地图左上角的像素坐标
// cb 是传入的回调函数,回调函数返回的参数是选中的 feature 对象数组,对象的字段和用户传入的数据相关
5.1.3 事件

鼠标事件回调参数 target

  • x: number 鼠标  在地图位置 x 坐标
  • y: number 鼠标  在地图位置 y 坐标
  • type: string 鼠标事件类型
  • lngLat: 经度度对象 {lng:number, lat: number }; 鼠标所在位置经纬度
  • feature: any; 数据选中的地理要素信息
  • featureId: number | null; 数据选中的地理要素的 ID

click

点击事件

mousemove

鼠标移动事件

mouseout

鼠标移除

mouseup

鼠标按下

mousedown

鼠标向下

contextmenu

鼠标右键

unclick

点击未拾取到元素

unmousemove

鼠标移动未拾取到元素

unmouseup

鼠标抬起未拾取到元素

unmousedown

鼠标按下未拾取到元素

uncontextmenu

鼠标右键位拾取到元素

unpick

所有鼠标事件未拾取到

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
layer.on('click', (ev) => {}); // 鼠标左键点击图层事件
layer.on('mouseenter', (ev) => {}); // 鼠标进入图层要素
layer.on('mousemove', (ev) => {}); // 鼠标在图层上移动时触发
layer.on('mouseout', (ev) => {}); // 鼠标移出图层要素时触发
layer.on('mouseup', (ev) => {}); // 鼠标在图层上单击抬起时触发
layer.on('mousedown', (ev) => {}); // 鼠标在图层上单击按下时触发
layer.on('contextmenu', (ev) => {}); // 图层要素点击右键菜单

// 鼠标在图层外的事件
layer.on('unclick', (ev) => {}); // 图层外点击
layer.on('unmousemove', (ev) => {}); // 图层外移动
layer.on('unmouseup', (ev) => {}); // 图层外鼠标抬起
layer.on('unmousedown', (ev) => {}); // 图层外单击按下时触发
layer.on('uncontextmenu', (ev) => {}); // 图层外点击右键
layer.on('unpick', (ev) => {}); // 图层外的操作的所有事件

inited 图层事件

参数 option

  • target 当前 layer
  • type 事件类型

图层初始化完成后触发

1
layer.on('inited', (option) => {});

add 图层事件

图层添加到 scene

参数 option

  • target 当前 layer
  • type 事件类型

remove 图层事件

图层移除时触发

参数 option

  • target 当前 layer

  • type 事件类

5.2 点图层

5.2.1 PointLayer 介绍

点数据的展示,数据源支持 JSON,GeoJSON,CSV 三种数据格式。

shape 支持

  • 3D 类型 柱图
1
'cylinder', 'triangleColumn', 'hexagonColumn', 'squareColumn'
  • 2D 符号图**
1
'circle', 'square', 'hexagon', 'triangle',  'pentagon',  'octogon', 'hexagram','rhombus',  'vesica'

基本图形代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import { PointLayer } from 'cci-map';
// import { PointLayer } from 'cci-layers'

const layer = PointLayer({
zIndex: 2,
})
.source(data.list, {
type: 'array',
x: 'j',
y: 'w',
})
.shape('cylinder')
.size('t', (level) => {
return [4, 4, level + 40];
})
.color('t', [
'#002466',
'#105CB3',
'#2894E0',
'#CFF6FF',
'#FFF5B8',
'#FFAB5C',
'#F27049',
'#730D1C',
])
5.2.2 气泡图
5.2.2.1 基础使用

气泡图通过 PointLayer 对象实例化,

1
2
import { PointLayer } from 'cci-map';
// import { PointLayer } from 'cci-layers'
5.2.2.2 shape

通常气泡图 shape 设置为 circle

5.2.2.3 size

气泡图大小,需要指定数据映射字段

1
2
3
4
5
6
7
8
9
const bubble = new PointLayer()
.source(data)
.shape(circle)
.size('mag', [0, 25])
.color('red')
.style({
opacity: 0.3,
strokeWidth: 1,
});
5.2.2.4 animate

气泡图支持水波动画效果

1
2
3
// 开启关闭动画
layer.animate(true);
layer.animate(false);
5.2.2.5 水波配置项
  • speed 水波速度
  • rings 水波环数
5.2.2.6 tips

目前 style 的配置项只支持全局设置,不支持数据映射。

如果 opacity 设置为 0.3 则所有的气泡都是 0.3

style 方法如果没有设置 stroke, 默认于气泡的填充色相同

5.2.3 散点图

在地理区域上放置相等大小的圆点,用来表示地域上的空间布局或数据分布。

5.2.3.1 使用

散点图通过 PointLayer 对象实例化

5.2.3.2 shape
  • circle
  • square
  • hexagon
  • triangle
  • pentagon
  • octogon
  • hexagram
  • rhombus
  • vesica

散点图 shape 一般设置成常量

5.2.3.3 color

color 可以根据数据的差异设置成不同颜色,表示数据的不同分类。

5.2.3.4 size

散点图一般等大小的图形, size 一般设置成常量

1
2
3
4
5
6
7
8
9
const scatter = new PointLayer()
.source(data)
.shape(circle)
.size(5)
.color('red')
.style({
opacity: 0.3,
strokeWidth: 1,
});
5.2.4 亮度图

亮度图又称点密度图,单位面积的内点的个数越多,亮度会越亮,亮度图一般用来表达海量点数据分布情况

5.2.4.1 shape
  • dot 如果需要使用亮度图可以将 shape 设置为 dot,或者不设置 shape
5.2.4.2 color
  • 无权重 如果数据没有权重可以将颜色设置为常量,渲染时会自动进行颜色叠加,点越多颜色越亮
  • 有权重 如果数据有权重可以设置一组同一色相,不同亮度的色带,值越大亮度会越亮。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
const pointLayer = new PointLayer()
.source(data)
.size(2)
.shape('dot')
.color('h8', [
'#0A3663',
'#1558AC',
'#3771D9',
'#4D89E5',
'#64A5D3',
'#72BED6',
'#83CED6',
'#A6E1E0',
'#B8EFE2',
'#D7F9F0',
])
.style({
opacity: 1,
});

scene.addLayer(pointLayer);
5.2.5 符号图

在地理区域上放置不同图片作为符号,通常表示不同地理要素分布情况

5.2.5.1 使用

符号图 通过 PointLayer 对象实例化,将 shape 设置成图片符号

5.2.5.2 shape

通过 scene addImage 方法

addImage() 参数:

  • id 图片的 id,
  • url 图片的 url
1
2
3
4
scene.addImage(
'00',
'https://gw.alipayobjects.com/zos/basement_prod/604b5e7f-309e-40db-b95b-4fac746c5153.svg',
);

⚠️ 符号图的 ID 不能与点图层已有 shape 名称相同,比如不能设置 circle

符号图需要把 shape 设置成图片的 id,同样符号图 shape 也支持数据映射

1
2
3
4
5
6
7
8
9
const scatter = new PointLayer()
.source(data)
.shape('00')
.size(5)
.color('red')
.style({
opacity: 0.3,
strokeWidth: 1,
});
5.2.6 文本标注

为图层添加文本标注

5.2.6.1 使用

地图标注需要添加一个新的图层的实现

5.2.6.2 shape
  • field 标注的字段名称
  • shapeType ‘text’
1
layer.shape('name', 'text');
5.2.6.3 style
  • opacity number
  • textAnchor string 文本相对锚点的位置 'right' | 'top-right' | 'left' | 'bottom-right' | 'left' | 'top-left' | 'bottom-left' | 'bottom' | 'bottom-right' | 'bottom-left' | 'top' | 'top-right' | 'top-left' | 'center';
  • padding: number 文本包围盒 padding [水平,垂直],影响碰撞检测结果,避免相邻文本靠的太近
  • spacing: number 文本间隔
  • stroke: string; 描边颜色
  • strokeWidth number 描边宽度
  • strokeOpacity number 描边透明度
  • fontWeight string 字体粗细
  • fontFamily string 字号
  • textOffset [number, number] 文本偏移量
  • textAllowOverlap: boolean 是否允许文字遮盖
5.2.7 聚合图
5.2.7.1 使用

目前只有点数据支持聚类方法

数据聚合主要从数据层数据,因此需要在 Source 方法配置 cluster 参数

5.2.7.2 Source

参见数据 source 部分

5.2.7.3 配置项
  • cluster boolean 是否聚合
  • clusterOption 聚合配置项
    • radius 聚合半径 number default 40
    • minZoom: 最小聚合缩放等级 number default 0
    • maxZoom: 最大聚合缩放等级 number default 16

数据聚合之后,数据会增加 pointcount 属性,在可视化渲染时可以根据 pointcount 进行数据映射。

5.2.7.4 方法
  • getClusters(zoom: number)

获取指定缩放等级的聚合数据

参数:zoom 缩放等级

  • getClustersLeaves(id: string)

根据 id 获取聚合节点的数据,每个聚合节点会有一个唯一 ID

参数:id 聚合数据 id

1
2
3
4
5
6
7
8
9
10
11
layer.source(pointsData, {
cluster: true,
});

// 设置配置项
layer.source(pointsData, {
cluster: true,
clusterOption: {
radius: 40,
},
});
  • getClustersLeaves

获取聚合节点的原始数据

1
2
3
4
5
const source = layer.getSource();
source.getClustersLeaves(id);
layer.on('click', (e) => {
console.log(source.getClustersLeaves(e.feature.cluster_id));
});
5.2.7.5 tips

PointLayer 的聚合图采用 WebGL 绘制,不支持自定义具体聚合样式,如果有自定义的需求可以使用 MarkerLayer 的聚合功能,你可以通过 Dom 完全自定义样式

5.2.8 3D 柱状图

3D 柱图地理区域上方会显示不同高度的柱体,主题的高度与其在数据集中的数值会成正比。

5.2.8.1 使用

3D 柱图通过 PointLayer 对象实例化,将 shape 设置成不同的 3Dshape

5.2.8.2 shape

3D Shape 支持

  • cylinder
  • triangleColumn
  • hexagonColumn
  • squareColumn
5.2.8.3 size

3D 柱图 size 需要设置三个维度 [w, l, z]

  • w 宽
  • l 长
  • z 高度

size 设置成常量

1
layer.size([2,2,3])

size 回调函数设置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
 layer.size('unit_price', h => {
return [ 6, 6, h / 500 ];
})
const column = new PointLayer({})
.source(data)
.shape('name', [
'cylinder',
'triangleColumn',
'hexagonColumn',
'squareColumn',
])
.size('unit_price', (h) => {
return [6, 6, h / 500];
})
.color('name', ['#5B8FF9', '#70E3B5', '#FFD458', '#FF7C6A'])
.style({
opacity: 1.0,
});

5.3 线图层

5.3.1 LineLayer
5.3.1.1 shape

线图层支持 4 种 shape

  • line 绘制路径图,
  • arc 绘制弧线 通过贝塞尔曲线算法技术弧线
  • greatcircle 大圆航线,地图两个点的最近距离不是两个点连线,而是大圆航线
  • arc3d 3d 弧线地图 3D 视角

⚠️ 弧线只需要设置起止点坐标即可

1
2
3
4
5
6
7
8
9
10
new LineLayer()
.source(data, {
parser: {
type: 'csv',
x: 'lng1',
y: 'lat1',
x1: 'lng2',
y1: 'lat2',
},
})

如果 geojson 数据绘制弧线图 coordinates 第一对坐标为起点,第二对为终点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "LineString",
"coordinates": [
[
106.5234375,
57.51582286553883
],
[
136.40625,
61.77312286453146
]
]
}
}
]
}
5.3.1.2 size

线图层 可以设置高度

  • size 类型为 number 则表示 line 的宽度
  • size 类型为 [number , number] 分别表示宽度和高度
1
2
lineLayer.size(1); // 线的宽度为 1
lineLayer.size([1, 2]); // 宽度为1,高度2
5.3.1.3 设置渐变色

线图层通过在 style 中设置起始颜色和终点颜色来设置颜色渐变,渐变色的优先级比 color 方法设置的颜色更高

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const layer = new LineLayer({})
.source(data, {
parser: {
type: 'csv',
x: 'lng1',
y: 'lat1',
x1: 'lng2',
y1: 'lat2',
},
})
.size(1)
.shape('arc')
.color('#8C1EB2')
.style({
sourceColor: '#f00', // 起点颜色
targetColor: '#0f0' // 终点颜色
});
5.3.1.4 animate

开启关闭动画

1
2
layer.animate(true);
layer.animate(false);

设置动画参数

  • duration 动画时间 单位(s)秒
  • interval 轨迹间隔, 取值区间 0 - 1
  • trailLength 轨迹长度 取值区间 0 - 1
1
2
3
4
5
layer.animate({
duration: 4,
interval: 0.2,
trailLength: 0.1,
});

参数动画介绍

目前动画参数为相对单位,我们默认一条线段的长度为 1 L7 动画参数

如果 interval = 0.2,则一条轨迹将会分成 5 段,如果 interval = 0.5 则为两段。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const layer = new LineLayer({})
.source(data, {
parser: {
type: 'csv',
x: 'lng1',
y: 'lat1',
x1: 'lng2',
y1: 'lat2',
},
})
.size(1)
.shape('arc')
.color('#8C1EB2')
.style({
opacity: 0.8,
});
5.3.2 弧线图
5.3.2.1 数据

绘制弧线只需提供起止点坐标即可(起止点调换位置,弧线的形状会对称相反,飞线动画的方向也会相反)

1
2
3
4
5
6
7
8
9
source(data, {
parser: {
type: 'csv',
x: 'lng1',
y: 'lat1',
x1: 'lng2',
y1: 'lat2',
},
});
5.3.2.2 shape

弧线支持三种弧线算法

  • arc 绘制弧线 通过贝塞尔曲线算法技术弧线
  • greatcircle 大圆航线,地图两个点的最近距离不是两个点连线,而是大圆航线
  • arc3d 3d 弧线地图 3D 视角
5.3.2.3 animate

参见线图层通用 animate 设置

5.3.2.4 示例代码

弧线地图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
import { CMap, LineLayer, Mapbox } from 'cci-map';
// import { CMap, GaodeMap } from 'cci-map';
// import { PointLayer } from 'cci-layers';
const scene = new CMap({
id: 'map',
map: new Mapbox({
style: 'dark',
pitch: 0,
center: [ 107.77791556935472, 35.443286920228644 ],
zoom: 2.9142882493605033
})
});
scene.on('loaded', () => {
fetch('https://gw.alipayobjects.com/os/rmsportal/UEXQMifxtkQlYfChpPwT.txt')
.then(res => res.text())
.then(data => {
const layer = new LineLayer({})
.source(data, {
parser: {
type: 'csv',
x: 'lng1',
y: 'lat1',
x1: 'lng2',
y1: 'lat2'
}
})
.size(1)
.shape('arc')
.color('#8C1EB2')
.style({
opacity: 0.8,
blur: 0.99
});
scene.addLayer(layer);
});
});
5.3.3 路径图
5.3.3.1 shape

shape 设置成 line 即可绘制路线图

  • line
5.3.3.2 size

路径图线的 size 支持两个维度

  • width 宽度
  • height 高度
1
2
3
layer.size([2, 10]); // 绘制宽度为2,高度为10的路径

layer.size('height', []);

5.4 面图层

5.4.1 PolygonLayer

绘制 2D 多边形以及沿 Z 轴拉伸后的 3D 图形。

5.4.1.1 使用
1
import { PolygonLayer } from 'cci-map';
5.4.1.2 shape

填充图支持 3 种 shape

  • fill 绘制填充面 不支持数据映射
  • line 绘制填充图描边 不支持数据映射
  • extrude 对填充图 3D 拉伸 不支持数据映射
1
2
3
PolyonLayer.shape('fill');
PolyonLayer.shape('line').size(1); // size 表示线宽度
PolyonLayer.shape('extrude').size(10); // size 表示高度
5.4.2 3D填充图
5.4.2.1 使用
1
2
import { PolygonLayer } from 'cci-map';
const layer = new PolygonLayer();
5.4.2.2 shape

3D Polygon 将多边形沿 Z 轴向上拉伸

  • extrude 常量不支持数据映射
1
layer.shape('extrude');
5.4.2.3 size

size 代表拉伸的高度,支持数据映射

1
2
3
4
5
6
layer.size(10); // 高度设置成常量
layer.size('floor', [0, 2000]); // 根据floor字段进行数据映射默认为线
layer.size('floor', (floor) => {
// 通过回调函数设置size
return floor * 2;
});
5.4.3 填充图
5.4.3.1 使用
1
2
3
import { PolygonLayer } from 'cci-map';
// import { PointLayer } from 'cci-layers';
const layer = new PolygonLayer();
5.4.3.2 shape

绘制填充图,shape 为fill 常量不支持数据映射

1
layer.shape('fill');
5.4.3.3 size

填充图无 size 不需要设置 size

5.5 城市建筑

5.5.1 使用
1
2
import { CityBuildingLayer } from 'cci-map';
// import { CityBuildingLayer } from 'cci-layers';
5.5.2 style
  • baseColor 楼房颜色,
  • windowColor: 窗户颜色,
  • brightColor: 点亮窗户颜色

其他 style 配置项同图层基类 style 配置

5.5.3 animate

开启动画效果

1
layer.animate(true);

自定义动画频率需 关闭默认动画,通过 setLight 方法不断更新时间

1
layer.animate(false);
  • setLight(time)

参数 time : 时间 毫秒

5.5.4 完整代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const pointLayer = new CityBuildingLayer();
pointLayer
.source(await response.json())
.size('floor', [0, 500])
.color('rgba(242,246,250,1.0)')
.animate({
enable: true,
})
.style({
opacity: 1.0,
baseColor: 'rgb(16,16,16)',
windowColor: 'rgb(30,60,89)',
brightColor: 'rgb(255,176,38)',
});
scene.addLayer(pointLayer);

5.6 图片图层

将图片添加到地图上,需要指定图片的经纬度范围

5.6.1 使用
1
2
import { ImageLayer } from 'cci-map';
// import { ImageLayer } from 'cci-layers'
5.6.2 代码示例
1
2
3
4
5
6
7
8
9
10
const layer = new ImageLayer({});
layer.source(
'https://gw.alipayobjects.com/zos/rmsportal/FnHFeFklTzKDdUESRNDv.jpg',
{
parser: {
type: 'image',
extent: [121.168, 30.2828, 121.384, 30.4219],
},
},
);

5.7 栅格图层

Raster 图层主要实现栅格数据的可视化,栅格数据主要来源是卫星遥感数据,如数字高程图,植被分布图,夜光图。

外部需要提前对栅格数据格式 如 tiff 进行解析,好做为 Source 传入。

5.7.1 使用
1
2
import { RasterLayer } from 'cci-map'
// import { RasterLayer } from 'cci-layers'
5.7.2 source

参见数据 source 的 raster 部分

5.7.3 shape

raster

5.7.4 size

无

5.7.5 color

无

5.7.6 style
  • clampLow Boolean 默认 false, 设置为 true,低于 domain 的数据将不显示

  • clampHigh Boolean 默认 false, 设置为 true,高于 domain 的数据将不显示

  • opacity: 0.8 Number 透明度

  • domain: [ 0, 8000 ] 数据映射区间

  • noDataValue Number noDataValue 不会显示

  • rampColors: { colors: [ ‘#FF4818’, ‘#F7B74A’, ‘#FFF598’, ‘#91EABC’, ‘#2EA9A1’, ‘#206C7C’ ], positions: [ 0, 0.2, 0.4, 0.6, 0.8, 1.0 ] } // 色带

    ⚠️ color, position 的长度要相同

5.8 热力图

5.8.1 经典热力图
5.8.1.1 使用
1
2
import { HeatmapLayer } from 'cci-map';
// import { HeatmapLayer } from 'cci-layers'
5.8.1.2 shape

常量 heatmap

1
layer.shape('heatmap');
5.8.1.3 size
  • field: 热力图权重字段
  • values: 数据映射区间 热力图显示 [0, 1] 效果最佳
1
layer.size('weight', [0, 1]);
5.8.1.4 color

heatmap 需要设置 color 方法,样式通过 style 设置

5.8.1.5 style
  • intensity 全局热力权重 推荐权重范围 1-5

  • radius 热力半径,单位像素

  • rampColors 色带参数

    • colors 颜色数组
    • positions 数据区间

    ⚠️ color, position 的长度要相同

5.8.1.6 完整代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
heatmapLayer()
.source(data)
.size('mag', [0, 1]) // weight映射通道
.style({
intensity: 3,
radius: 20,
rampColors: {
colors: [
'rgba(33,102,172,0.0)',
'rgb(103,169,207)',
'rgb(209,229,240)',
'rgb(253,219,199)',
'rgb(239,138,98)',
'rgb(178,24,43,1.0)',
],
positions: [0, 0.2, 0.4, 0.6, 0.8, 1.0],
},
});
5.8.2 网格热力图

将一组点数据按照等大小的正方形网格进行聚合,一个正方形网格代表网格内所有点的统计值。方格热力图特点以方格网布局。

5.8.2.1 使用
1
2
import { HeatmapLayer } from 'cci-map';
// import { HeatmapLayer } from 'cci-layers'
5.8.2.2 source

网格数据只支持点数据作为数据源,数据格式支持 csv、json、geojson.

5.8.2.3 设置网格聚合参数

布局方法 通过 source 的 tansforms 属性配置

  • type 数据聚合类型 grid
  • size 网格半径 单位 米
  • field 聚合字段
  • method 聚合方法 count,max,min,sum,mean 5 个统计维度
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
layer.source(data, {
parser: {
type: 'csv',
x: 'lng',
y: 'lat',
},
transforms: [
{
type: 'grid',
size: 15000,
field: 'v',
method: 'sum',
},
],
});
5.8.2.4 shape

网格热力图虽然是以标准四边形网格进行数据聚合,但是展示效果上可以设置为其形状,形状只支持常量

2d

  • circle,
  • triangle
  • square
  • heaxgon
1
layer.shape('circle');

3d

  • cylinder
  • triangleColumn
  • hexagonColumn
  • squareColumn,
1
layer.shape('cylinder');
5.8.2.5 size
  • 2D shape

不需要设置 size 方法

  • 3D 图形

size 表示高度, 支持常量和数据映射

1
2
3
layer.size(10); // 常量
layer.size('value', [10, 50]); // 根据value 字段映射大小
layer.size('value', (value) => {}); // 回调函数设置高度
5.8.2.6 color

同图层基类 color 方法

5.8.2.7 style
  • coverage 网格覆盖度 0 - 1
  • angle 网格旋转角度 0 - 360
  • opacity 透明度 0 - 1.0
1
2
3
4
5
layer.style({
coverage: 0.9,
angle: 0,
opacity: 1.0,
});
5.8.2.8 完整实例代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
const layer = new HeatmapLayer({})
.source(data, {
parser: {
type: 'csv',
x: 'lng',
y: 'lat',
},
transforms: [
{
type: 'grid',
size: 20000,
field: 'v',
method: 'sum',
},
],
})
.shape('square')
.style({
coverage: 1,
angle: 0,
})
.color('count', [
'#0B0030',
'#100243',
'#100243',
'#1B048B',
'#051FB7',
'#0350C1',
'#0350C1',
'#0072C4',
'#0796D3',
'#2BA9DF',
'#30C7C4',
'#6BD5A0',
'#A7ECB2',
'#D0F4CA',
]);

scene.addLayer(layer);
5.8.3 蜂窝热力图

将一组点数据按照等大小的六边形网格进行聚合,一个六边形网格代表网格内所有点的统计值。蜂窝热力图特点以六边形热力图网格布局

5.8.3.1 使用
1
2
import { HeatmapLayer } from 'cci-map';
// import { HeatmapLayer } from 'cci-layers'
5.8.3.2 source

网格数据只支持点数据作为数据源,数据格式支持 csv、json、geojson.

5.8.3.3 设置网格聚合参数

布局方法 通过 source 的 tansforms 属性配置

  • type 数据聚合类型 hexagon
  • size 网格半径 单位 米
  • field 聚合字段
  • method 聚合方法 count,max,min,sum,mean 5 个统计维度
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
layer.source(data, {
parser: {
type: 'csv',
x: 'lng',
y: 'lat',
},
transforms: [
{
type: 'hexagon',
size: 15000,
field: 'v',
method: 'sum',
},
],
});
5.8.3.4 shape

网格热力图虽然是以标准四边形网格进行数据聚合,但是展示效果上可以设置为其形状,形状只支持常量

2d

  • circle,
  • triangle
  • square
  • heaxgon
1
layer.shape('circle');

3d

  • cylinder
  • triangleColumn
  • hexagonColumn
  • squareColumn,
1
layer.shape('cylinder');
5.8.3.5 size
  • 2D shape

不需要设置 size 方法

  • 3D 图形

size 表示高度, 支持常量和数据映射

1
2
3
layer.size(10); // 常量
layer.size('value', [10, 50]); // 根据value 字段映射大小
layer.size('value', (value) => {}); // 回调函数设置高度
5.8.3.6 color

同图层基类 color 方法

5.8.3.7 style
  • coverage 网格覆盖度 0 - 1
  • angle 网格旋转角度 0 - 360
  • opacity 透明度 0 - 1.0
1
2
3
4
5
layer.style({
coverage: 0.9,
angle: 0,
opacity: 1.0,
});
5.8.3.8 完整代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
const layer = new HeatmapLayer({
zIndex: 2,
})
.souce(data, {
parser: {
type: 'csv',
x: lng,
y: lat,
},
transforms: [
{
type: 'hexagon',
size: 1500,
field: 'count',
operation: 'sum',
},
],
})
.shape('hexagon')
.color('sum')
.style({
coverage: 0.8,
});

6 第三方引擎接入

  • 支持允许用户接入第三方的渲染引擎对地图场景进行开发,threejs 作为当前最广泛使用的通用 3D 渲染引擎,将其集成后可以满足用户自定义开发的需求。

  • 目前 Three 模块为了抹平不同地图底图之间的差异,提供了一些兼容方法,如 setMeshScale 方法,通过这些方法用户可以在不同的底图 环境中使用同一套代码。

  • 目前提供的适配方法只负责 threejs 世界坐标到不同地图底图坐标的转化、 gl 上下文的共享以及渲染帧的同步,其余关于 3D 场景内容的搭建 与普通 threejs 应用的开发没有任何区别。

  • 本身并没有集成 threejs,所以用于在使用 Three 模块的时候需要独立安装 threejs。

✨ 目前官方提供的 threejs 兼容是根据 0.115.0 版本进行开发的,使用其他版本 threejs 可能会存在兼容问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// 1、引入对应模块
import { ThreeLayer, ThreeRender } from 'cci-map';
// import { ThreeLayer, ThreeRender } from 'cci-three'
import * as THREE from 'three';
...
// 2、注册服务
scene.registerRenderService(ThreeRender);
...
// 3、构建 threejs 图层对象并在其中添加 threejs 构建的网格对象
const threeJSLayer = new ThreeLayer({
onAddMeshes: (threeScene: THREE.Scene, layer: ThreeLayer) => {
threeScene.add(new THREE.AmbientLight(0xffffff));

const sunlight = new THREE.DirectionalLight(0xffffff, 0.25);
sunlight.position.set(0, 80000000, 100000000);
sunlight.matrixWorldNeedsUpdate = true;
threeScene.add(sunlight);

let center = scene.getCenter();

let cubeGeometry = new THREE.BoxBufferGeometry(10000, 10000, 10000);
let cubeMaterial = new THREE.MeshNormalMaterial();
let cube = new THREE.Mesh(cubeGeometry, cubeMaterial);

layer.setObjectLngLat(cube, [center.lng + 0.05, center.lat], 0);
threeScene.add(cube);
},
})
.source(data)
.animate(true);

// 4、添加 threejs 图层对象
scene.addLayer(threeJSLayer);

cci-map 将 threejs 的引用封装成一个特殊的图层对象,在使用上与其他的图层相同。

6.1 构造函数 new ThreeLayer

onAddMeshes

该方法接受两个参数 threeScene: THREE.Scene, layer: ThreeLayer

  • threeScene: 这是普通的 threejs 场景对象
  • layer: 提供的 threeLayer 对象,上面挂载了 threejs 空间适配到地图空间所需要的方法

6.2 ThreeLayer

用户新建的图层对象,同时也会在 onAddMesh 方法的第二个参数返回。

以下是挂载在 ThreeLayer 实例上的适配方法。

getModelMatrix(lnglat, altitude, rotation, scale): Matrix

  • lnglat: [number, number] 经纬度
  • altitude: number = 0 相对高度
  • rotation: [number, number, number] = [0, 0, 0] 旋转角度
  • scale: [number, number, number] = [1, 1, 1] 缩放比例

用户可以通过该方法计算在对应经纬度点位、相对高度、旋转角度和缩放的模型矩阵 该方法的返回值是 THREE.Matrix4 类型的矩阵

applyObjectLngLat(object, lnglat, altibute): void

  • object: Object3D threejs 对象
  • lnglat: ILngLat[number, number] 经纬度
  • altitude = 0 相对高度

用户可以通过该方法将 object 对象从当前位置向指定位置移动(地图经纬度坐标)

setObjectLngLat(object, lnglat, altibute): void

  • object: Object3D threejs 对象
  • lnglat: ILngLat[number, number] 经纬度
  • altitude = 0 相对高度

用户可以通过该方法设置 object 对象的位置(地图经纬度坐标)

lnglatToCoord(lnglat): [number, number]

  • lnglat: ILngLat[number, number] 经纬度

用户可以通过该方法将经纬度坐标转化成 threejs 世界坐标

adjustMeshToMap(object): void

  • object: Object3D threejs 对象

用户在添加 threejs 对象的前可以通过该方法调整 3D 对象的姿态,保证添加对象能正确显示

✨ 在 threejs 世界坐标中,默认的上方向为 Y 轴正方向,而在地图坐标中,默认的上方向为 Z 轴正方向

✨ 用户不一定使用该方法调整物体的姿态,也可以自己实现

setMeshScale(object, x, y, z): void

  • object: Object3D threejs 对象
  • x: number = 1 x 轴方向的缩放比例
  • y: number = 1 y 轴方向的缩放比例
  • z: number = 1 z 轴方向的缩放比例

用户可以通过该方法设置 threejs 对象缩放

✨ 其实通过设置 threejs 对象的 scale 属性一样能达到同样的效果,但是由于 mapbox 在计算模型矩阵的时候引入了特殊计算,所以无法直接设置 scale 属性进行缩放

✨ 同样的,可以直接修改 threejs 的 position、rotation 等调整 3D 对象的姿态

addAnimateMixer(mixer): void

  • mixer: AnimationMixer threejs 的动画混合器

用户通过该方法管理加载模型的动画

getRenderCamera(): THREE.Camera

返回根据当前地图场景参数下对应的 THREEJS 相机

6.3 加载模型

用户可以使用 threejs 提供的能力加载其支持的任意模型

✨ 以加载 gltf 模型为例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// 1、引入加载器
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
...
const threeJSLayer = new ThreeLayer({
onAddMeshes: (threeScene: THREE.Scene, layer: ThreeLayer) => {
...
// 2、构建加载器
const loader = new GLTFLoader();
// 3、加载模型
loader.load('https://gw.alipayobjects.com/os/bmw-prod/3ca0a546-92d8-4ba0-a89c-017c218d5bea.gltf',
(gltf) => {
const model = gltf.scene;
layer.adjustMeshToMap(model);
layer.setMeshScale(model, 1000, 1000, 1000);
layer.setObjectLngLat( model, [center.lng, center.lat], 0 );

// 4、播放模型上绑定的动画
const animations = gltf.animations;
if (animations && animations.length) {
const mixer = new THREE.AnimationMixer(model);
const animation = animations[2];
const action = mixer.clipAction(animation);
action.play();
// 5、由 cci-map 控制模型动画的播放
layer.addAnimateMixer(mixer);
}
})
}
}).source(data)
.animate(true) // 若需要播放模型动画,请开启动画模式(或者场景中已经存在开启动画的图层)

八、服务

1 WFS

1.1 介绍

OGCWebFeatureService(WFS)接口标准定义了一组接口,用于在Internet上访问要素和要素属性级别的地理信息。特征是对现实世界现象的抽象,也就是说,它代表了世界上可以找到的任何事物。地理要素的属性或特征称为要素特性。WFS提供了检索或查询地理要素的方法,这种方法独立于它们发布的底层数据存储。如果WFS被授权这样做,该服务还可以更新或删除地理特征。WFS的实例还能够存储查询,以便使客户机应用程序能够在稍后的时间点检索或执行查询。

1.1.1 历史

WFS于2002年5月发布了1.0.0版,2005年5月发布了1.1.0版,2010年11月发布了2.0.0版。版本2.0.0是ISO 19142的基础。OGC于2014年7月发布了WFS版本2.0.2。

1.1.2 版本

2.0.2是当前最新版本

1.1.3 测试套件
  • WFS 1.1.0版
  • WFS 2.0.0版
1.1.4 使用

WFS标准通过高度可配置的界面提供地理要素数据。默认情况下,WFS返回的数据使用地理标记语言(GML),GML是作为可扩展标记语言(XML)编写的。不过,该标准的新版本也将支持JavaScript对象表示法(JSON)。政府机构、私人组织和学术机构使用本标准发布矢量地理空间数据集,以便于接收机构编制新地图或对所提供数据进行分析。

WFS为请求由地理要素及其属性组成的矢量地理空间数据提供了一个标准接口。这样做的好处是,WFS客户端可以从多个WFS服务器请求源数据,然后呈现数据以在客户端上显示,或者作为工作流的一部分进一步处理数据。该标准保证使用公共地理空间坐标参考系,可以将数据与其他数据一致地处理。使用常见数据类型(如文本字符串、日期和时间)编码的特性属性也可以得到一致的处理。

1.1.5 与其他OGC标准的关系
  • OGC网络地图块服务接口标准(WMS):WMS标准更适合于需要渲染地图而不是源向量数据的地方。
  • Web地图块服务接口标准(WMTS):WMTS标准更适合于发出许多并发请求的高度可伸缩系统需要静态呈现的地图。
  • OGC网络地图块服务接口标准(WCS):WCS标准更适合于需要源覆盖数据(如栅格图像)的情况。
  • OGC地理标记语言(GML):这个标准被WFS用作发布数据的默认编码格式。

1.2 WFS 操作概述

WFS指定了许多不同的操作,所有服务器都需要支持这些操作:

  • GetCapabilities

    返回一个文档,该文档描述由服务器提供的WFS服务提供的功能和资源。

  • DescribeFeatureType

    返回WFS实例提供或接受的功能类型和功能属性的结构描述。

  • ListStoredQueries

    返回存储在WFS实例中的查询列表。

  • DescribeStoredQueries

    返回存储在WFS实例中的查询的说明。

  • GetFeature

    从通过WFS发布的数据存储中返回要素实例的选择。

WFS服务器还可以提供以下可选操作:

  • GetPropertyValue

    检索一组要素实例的要素特性值或复杂要素特性值的一部分

  • GetFeatureWithLock

    提供与GetFeature请求类似的功能,但具有锁定特性的附加功能,可能是为了后续更新或更改。

  • LockFeature

    锁定一组要素实例,以便在锁定到位时,其他操作都不能修改数据。

  • 交易

    允许插入、更新或删除要素实例及其特性。

  • CreateStoredQuery

    创建并存储一个查询,客户机可以在稍后的时间点快速轻松地触发该查询。

  • DropStoredQuery

    从服务器中删除以前存储的查询

1.3 WFS 操作详细信息

提供有关WFS服务器提供的操作类型的详细信息。该列表包括由WFS服务器的不同配置(正式称为一致性类)提供的操作。

Operation Description
GetCapabilities 检索有关服务的元数据,包括支持的操作和参数,以及可用功能类型的列表。
DescribeFeatureType 返回WFS实例提供或接受的功能类型和功能属性的结构描述。
GetFeature 从通过WFS发布的数据存储中返回要素实例的选择。
ListStoredQueries 返回存储在WFS实例中的查询列表。
DescribeStoredQueries 返回存储在WFS实例中的查询的说明。
GetPropertyValue (可选) 检索一组要素实例的要素特性值或复杂要素特性值的一部分
GetFeatureWithLock (可选) 提供与GetFeature请求类似的功能,但具有锁定特性的附加功能,可能是为了后续更新或更改。
LockFeature (可选) 锁定一组要素实例,以便在锁定到位时,其他操作都不能修改数据。
Transaction (可选) 允许修改或删除要素实例及其属性。
CreateStoredQuery (可选) 创建并存储一个查询,客户机可以在稍后的时间点快速轻松地触发该查询。
DropStoredQuery (可选) 从服务器中删除以前存储的查询。

以下是可以发送到由简单和基本的WFS配置提供的操作的请求的示例(仅列出 GetFeature 和数据更新操作)

1.3.1 GetFeature(用于数据查询)

WFS服务器响应 GetFeature 请求返回根据请求客户端设置的条件筛选的地理要素实例的集合。

请求可以通过httpget或httppost发送。为了简单起见,下面的示例请求仅通过httpget发送。

1.3.1.1 请求

最简单的GetFeature请求是下载特性集合而不受任何约束来过滤内容的请求。下面显示了此类请求的一个示例。这个 GetFeature request使用一组描述要返回的地理特征的参数来查询服务器。TYPENAMES参数确定要返回的要素实例集合。

1
2
3
4
5
http://cite.deegree.org/deegree-webservices-3.4-RC3/services/wfs200?
SERVICE=WFS&
VERSION=2.0.0&
REQUEST=GetFeature&
TYPENAMES=ps:ProtectedSite
1.3.1.2 响应

从上述请求得到的响应摘要如下所示。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<wfs:FeatureCollection
timeStamp="2017-09-21T18:59:13"
numberMatched="16"
numberReturned="16"
next="https://wfst.axl.aero/AxlRest/wfs?SERVICE=WFS&amp;VERSION=2.0.2&amp;REQUEST=GetFeature&amp;TYPENAMES=ps:ProtectedSite&amp;STARTINDEX=2&amp;COUNT=1"
xmlns:gts="http://www.isotc211.org/2005/gts"
xmlns:wfs="http://www.opengis.net/wfs/2.0"
xmlns:sch="http://purl.oclc.org/dsdl/schematron"
xmlns:xml="http://www.w3.org/XML/1998/namespace"
xmlns:base="urn:x-inspire:specification:gmlas:BaseTypes:3.2"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:gco="http://www.isotc211.org/2005/gco"
xmlns:fes="http://www.opengis.net/fes/2.0"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:ows="http://www.opengis.net/ows/1.1"
xmlns:gmx="http://www.isotc211.org/2005/gmx"
xmlns:gss="http://www.isotc211.org/2005/gss"
xmlns:ps="urn:x-inspire:specification:gmlas:ProtectedSites:3.0"
xmlns:gsr="http://www.isotc211.org/2005/gsr"
xmlns:gn="urn:x-inspire:specification:gmlas:GeographicalNames:3.0"
xmlns:smil20="http://www.w3.org/2001/SMIL20/"
xmlns:sqm="http://axl.avitech.aero/aviWfsSqm/1.0"
xmlns:gml="http://www.opengis.net/gml/3.2"
xmlns:smil20lang="http://www.w3.org/2001/SMIL20/Language"
xmlns:gmd="http://www.isotc211.org/2005/gmd"
xsi:schemaLocation="http://www.opengis.net/wfs/2.0 http://schemas.opengis.net/wfs/2.0/wfs.xsd http://www.opengis.net/gml/3.2 http://schemas.opengis.net/gml/3.2.1/gml.xsd">
<wfs:member xmlns:wfs="http://www.opengis.net/wfs/2.0">
<!-- oldId=none ;oldTime=1487178040 ; oldExpiry=0 ; ; currentTime=1506020133 ; expired=true;-->
<ps:ProtectedSite xmlns:gml="http://www.opengis.net/gml/3.2" xmlns:ps="urn:x-inspire:specification:gmlas:ProtectedSites:3.0" gml:id="PS_PROTECTEDSITE_16cbb8db-cce1-4e8b-8733-83d53a910ebe">
...
</ps:ProtectedSite>
</wfs:member></wfs:FeatureCollection>
1.3.1.3 计数参数

可添加其他参数以进一步筛选或转换来自WFS的响应。指定参数值所需的信息,包括下面介绍的其他参数。

要在上面的GetFeature请求中包含其他参数,只需在URL的末尾添加一个与号(&),然后添加参数名称、等号和要分配给参数的值。例如,下面的GetFeature请求将服务器返回的特性数限制为单个特性实例。限制响应的数量由count参数的值确定。

1
2
3
4
5
6
https://wfst.axl.aero/AxlRest/wfs?
service=WFS&
version=2.0.0&
request=GetFeature&
TypeNames=ps:ProtectedSite&
count=1

请求生成的响应如下所示。请注意,返回的特性实例的数量是1(由根目录中的numberReturned属性显示)wfs:FeatureCollection元素).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
<wfs:FeatureCollection
timeStamp="2017-09-21T18:55:33"
numberMatched="16"
numberReturned="1"
next="https://wfst.axl.aero/AxlRest/wfs?SERVICE=WFS&amp;VERSION=2.0.2&amp;REQUEST=GetFeature&amp;TYPENAMES=ps:ProtectedSite&amp;STARTINDEX=2&amp;COUNT=1"
xmlns:gts="http://www.isotc211.org/2005/gts"
xmlns:wfs="http://www.opengis.net/wfs/2.0"
xmlns:sch="http://purl.oclc.org/dsdl/schematron"
xmlns:xml="http://www.w3.org/XML/1998/namespace"
xmlns:base="urn:x-inspire:specification:gmlas:BaseTypes:3.2"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:gco="http://www.isotc211.org/2005/gco"
xmlns:fes="http://www.opengis.net/fes/2.0"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:ows="http://www.opengis.net/ows/1.1"
xmlns:gmx="http://www.isotc211.org/2005/gmx"
xmlns:gss="http://www.isotc211.org/2005/gss"
xmlns:ps="urn:x-inspire:specification:gmlas:ProtectedSites:3.0"
xmlns:gsr="http://www.isotc211.org/2005/gsr"
xmlns:gn="urn:x-inspire:specification:gmlas:GeographicalNames:3.0"
xmlns:smil20="http://www.w3.org/2001/SMIL20/"
xmlns:sqm="http://axl.avitech.aero/aviWfsSqm/1.0"
xmlns:gml="http://www.opengis.net/gml/3.2"
xmlns:smil20lang="http://www.w3.org/2001/SMIL20/Language"
xmlns:gmd="http://www.isotc211.org/2005/gmd"
xsi:schemaLocation="http://www.opengis.net/wfs/2.0 http://schemas.opengis.net/wfs/2.0/wfs.xsd http://www.opengis.net/gml/3.2 http://schemas.opengis.net/gml/3.2.1/gml.xsd">
<wfs:member xmlns:wfs="http://www.opengis.net/wfs/2.0">
<!-- oldId=none ;oldTime=1487178040 ; oldExpiry=0 ; ; currentTime=1506020133 ; expired=true;-->
<ps:ProtectedSite xmlns:gml="http://www.opengis.net/gml/3.2" xmlns:ps="urn:x-inspire:specification:gmlas:ProtectedSites:3.0" gml:id="PS_PROTECTEDSITE_16cbb8db-cce1-4e8b-8733-83d53a910ebe">
<gml:name>ProtectedSite1</gml:name>
<ps:geometry>
<gml:MultiSurface gml:id="PS_PROTECTEDSITE_16cbb8db-cce1-4e8b-8733-83d53a910ebe_PS_GEOMETRY" srsName="urn:ogc:def:crs:EPSG::4326">
<gml:surfaceMember>
<gml:Polygon gml:id="GEOMETRY_7fd8f3e7-d283-4ce5-9722-e612bf465a3b" srsName="urn:ogc:def:crs:EPSG::4326">
<gml:exterior>
<gml:LinearRing>
<gml:posList>
51.628734 5.507951 51.629015 5.508402 51.629694 5.507409 51.628721 5.507150 51.628734
5.507951
</gml:posList>
</gml:LinearRing>
</gml:exterior>
</gml:Polygon>
</gml:surfaceMember>
</gml:MultiSurface>
</ps:geometry>
<ps:inspireID>
<base:Identifier>
<base:localId>FB8DCB4E-03BB-4E8A-BB9D-04A44295DF58</base:localId>
<base:namespace>NL.9930.EHS</base:namespace>
</base:Identifier>
</ps:inspireID>
<ps:siteDesignation>
<ps:DesignationType>
<ps:designationScheme>ecologischeHoofdstructuur</ps:designationScheme>
<ps:designation>ecologischeHoofdstructuur</ps:designation>
<ps:percentageUnderDesignation>100</ps:percentageUnderDesignation>
</ps:DesignationType>
</ps:siteDesignation>
</ps:ProtectedSite>
</wfs:member></wfs:FeatureCollection>
1.3.1.4 边界框(BBOX)参数

可以添加到GetFeature请求的另一个参数是边界框(BBOX)的参数。此参数是一个逗号分隔的列表,由四个数字组成,表示应返回的要素实例的最小和最大边界坐标。下面显示了使用BBOX参数的示例。

1
http://cite.deegree.org/deegree-webservices-3.4-RC3/services/wfs200?service=WFS&version=2.0.0&request=GetFeature&TypeNames=ps:ProtectedSite&BBOX=51.607317,5.106151,51.629884,5.228022

上面的请求返回的响应如下所示。为了简洁起见,这里提供了一些缺少的内容。

1
2
3
4
5
6
7
<wfs:FeatureCollection xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:wfs="http://www.opengis.net/wfs/2.0" timeStamp="2017-09-21T18:14:50Z" xmlns:gml="http://www.opengis.net/gml/3.2" numberMatched="1" numberReturned="1">
<wfs:member>
<ps:ProtectedSite xmlns:ps="urn:x-inspire:specification:gmlas:ProtectedSites:3.0" gml:id="PS_PROTECTEDSITE_a367af0b-9457-4445-9cbd-eb48ae7a844a">
...
</ps:ProtectedSite>
</wfs:member>
</wfs:FeatureCollection>
1.3.1.5 PropetyName参数

可以添加到GetFeature请求的另一个参数是propertyName。此参数返回只包含指定属性的功能实例。在具有多个属性的功能类型通过带宽有限的网络提供服务的情况下,此功能尤其有用。客户机应用程序可以选择要在功能实例中返回的属性。下面显示了使用此参数的示例。

1
https://services.interactive-instruments.de/ogc-reference/simple/wfs?version=2.0.0&request=getfeature&service=wfs&typenames=ci:City&count=3&propertyName=inhabitants

上面的请求返回的响应如下所示。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<wfs:FeatureCollection timeStamp="2017-09-21T21:17:32.296+02:00" numberReturned="3" numberMatched="unknown" xmlns="http://www.interactive-instruments.de/namespaces/demo/cities/4.1/cities" xmlns:gml="http://www.opengis.net/gml/3.2" xmlns:wfs="http://www.opengis.net/wfs/2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<wfs:member>
<City gml:id="City.10">
<location>
<gml:Point gml:id="city.id.10.location.Geom_0" srsName="urn:ogc:def:crs:EPSG::25832" srsDimension="2">
<gml:pos>486890.340 5881020.962</gml:pos>
</gml:Point>
</location>
<inhabitants>547300</inhabitants>
</City>
</wfs:member>
<wfs:member>
<City gml:id="City.11">
<location>
<gml:Point gml:id="city.id.11.location.Geom_0" srsName="urn:ogc:def:crs:EPSG::25832" srsDimension="2">
<gml:pos>364883.268 5620035.394</gml:pos>
</gml:Point>
</location>
<inhabitants>324800</inhabitants>
</City>
</wfs:member>
<wfs:member>
<City gml:id="City.9">
<location>
<gml:Point gml:id="city.id.9.location.Geom_0" srsName="urn:ogc:def:crs:EPSG::25832" srsDimension="2">
<gml:pos>361905.275 5702287.179</gml:pos>
</gml:Point>
</location>
<inhabitants>574600</inhabitants>
</City>
</wfs:member>
</wfs:FeatureCollection>
1.3.1.6 sortBy参数

WFS返回的结果也可以通过sortBy参数按特定的顺序组织。此参数按指定参数确定的序列返回要素实例。在要素类型具有表示数量或总体的特性的情况下,此功能尤其有用。下面显示了使用此参数的示例。

1
https://services.interactive-instruments.de/ogc-reference/simple/wfs?version=2.0.0&request=getfeature&service=wfs&typenames=ci:City&sortBy=inhabitants&propertyName=inhabitants

由上述请求得到的响应如下所示。为了简洁起见,省略了一些内容。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<?xml version="1.0" encoding="utf-8"?>
<wfs:FeatureCollection timeStamp="2017-09-21T21:17:32.296+02:00" numberReturned="11" numberMatched="unknown" xmlns="http://www.interactive-instruments.de/namespaces/demo/cities/4.1/cities" xmlns:gml="http://www.opengis.net/gml/3.2" xmlns:wfs="http://www.opengis.net/wfs/2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<wfs:member>
<City gml:id="City.11">
<location>
<gml:Point gml:id="city.id.11.location.Geom_0" srsName="urn:ogc:def:crs:EPSG::25832" srsDimension="2">
<gml:pos>364883.268 5620035.394</gml:pos>
</gml:Point>
</location>
<inhabitants>324800</inhabitants>
</City>
</wfs:member>
<wfs:member>
<City gml:id="City.10">
<location>
<gml:Point gml:id="city.id.10.location.Geom_0" srsName="urn:ogc:def:crs:EPSG::25832" srsDimension="2">
<gml:pos>486890.340 5881020.962</gml:pos>
</gml:Point>
</location>
<inhabitants>547300</inhabitants>
</City>
</wfs:member>
<wfs:member>
<City gml:id="City.9">
<location>
<gml:Point gml:id="city.id.9.location.Geom_0" srsName="urn:ogc:def:crs:EPSG::25832" srsDimension="2">
<gml:pos>361905.275 5702287.179</gml:pos>
</gml:Point>
</location>
<inhabitants>574600</inhabitants>
</City>
</wfs:member>
</wfs:FeatureCollection>
1.3.1.7 srsName参数

此参数用于指定用于编码要素几何图形的空间参考系。可以从GetCapabilities响应中标识每个要素类型所允许的空间参考系统。

1
https://services.interactive-instruments.de/ogc-reference/simple/wfs?version=2.0.0&request=getfeature&service=wfs&typenames=ci:City&count=1&srsName=urn:ogc:def:crs:EPSG::4326

由上述请求得到的响应如下所示。为了简洁起见,省略了一些内容。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<wfs:FeatureCollection timeStamp="2017-09-21T21:26:02.213+02:00" numberReturned="1" numberMatched="unknown" xmlns="http://www.interactive-instruments.de/namespaces/demo/cities/4.1/cities" xmlns:gml="http://www.opengis.net/gml/3.2" xmlns:wfs="http://www.opengis.net/wfs/2.0" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<wfs:boundedBy>
<gml:Envelope srsName="urn:ogc:def:crs:EPSG::4326" srsDimension="2">
<gml:lowerCorner>52.521400003005 13.405700006106</gml:lowerCorner>
<gml:upperCorner>52.521400003005 13.405700006106</gml:upperCorner>
</gml:Envelope>
</wfs:boundedBy>
<wfs:member>
<City gml:id="City.1">
<name>Berlin</name>
<location>
<gml:Point gml:id="city.id.1.location.Geom_0" srsName="urn:ogc:def:crs:EPSG::4326" srsDimension="2">
<gml:pos>52.521400003005 13.405700006106</gml:pos>
</gml:Point>
</location>
<country>Germany</country>
<inhabitants>3460700</inhabitants>
<lzi>2011-03-17T15:32:54Z</lzi>
<function>capital</function>
</City>
</wfs:member>
</wfs:FeatureCollection>
1.3.1.8 featureId参数

此参数用于筛选请求返回的功能。

1
2
3
4
5
http://localhost:8080/geoserver/wfs?
request=GetFeature&
version=2.0.0&
typeName=topp:states&
FEATUREID=states.3

由上述请求得到的响应如下所示。为了简洁起见,省略了一些内容。

1
2
3
4
5
6
7
8
9
10
11
12
13
<wfs:FeatureCollection xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:wfs="http://www.opengis.net/wfs/2.0" xmlns:gml="http://www.opengis.net/gml/3.2" xmlns:topp="http://www.openplans.org/topp" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" numberMatched="1" numberReturned="1" timeStamp="2017-09-21T19:38:52.788Z">
<wfs:member>
<topp:states gml:id="states.3">
<topp:the_geom>
<gml:MultiSurface srsName="urn:ogc:def:crs:EPSG::4326" srsDimension="2">
<gml:surfaceMember><gml:Polygon><gml:exterior><gml:LinearRing><gml:posList>38.557476 -75.70742 38.649551 -75.71106 38.83017 -75.724937 39.141548 -75.752922 39.247753 -75.761658 38.450451 -75.093094 38.455208 -75.350204 38.463066 -75.69915 38.557476 -75.70742</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></gml:surfaceMember>
</gml:MultiSurface>
</topp:the_geom>
<topp:STATE_NAME>Delaware</topp:STATE_NAME>
<topp:SAMP_POP>102776.0</topp:SAMP_POP>
</topp:states>
</wfs:member>
</wfs:FeatureCollection>
1.3.1.9 查询操作

到目前为止,我们只展示了通过httpget方法作为url发送的GetFeature请求示例。也可以通过httppost方法将请求主体作为XML文档发送。

下面是一个GetFeature请求的示例,该请求包含一个查询操作,并通过httppost方法发送。

请求被发送到以下URL https://services.interactive-instruments.de/ogc-reference/simple/wfs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<wfs:GetFeature service="WFS" version="2.0.0"
xmlns:wfs="http://www.opengis.net/wfs/2.0" xmlns:ci="http://www.interactive-instruments.de/namespaces/demo/cities/4.1/cities" xmlns:fes="http://www.opengis.net/fes/2.0"
xmlns:sf="http://www.openplans.org/spearfish" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.opengis.net/wfs/2.0 http://schemas.opengis.net/wfs/2.0/wfs.xsd">
<wfs:Query typeNames="ci:City">
<wfs:PropertyName resolve="local">ci:inhabitants</wfs:PropertyName>
<fes:Filter>
<fes:PropertyIsLessThan>
<fes:ValueReference>ci:inhabitants</fes:ValueReference>
<fes:Literal>400000</fes:Literal>
</fes:PropertyIsLessThan>
</fes:Filter>
</wfs:Query>
</wfs:GetFeature>

上面的请求返回的响应如下所示。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<wfs:FeatureCollection timeStamp="2017-09-22T09:56:55.908+02:00" numberReturned="1" numberMatched="unknown" xmlns="http://www.interactive-instruments.de/namespaces/demo/cities/4.1/cities" xmlns:gml="http://www.opengis.net/gml/3.2" xmlns:wfs="http://www.opengis.net/wfs/2.0" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.interactive-instruments.de/namespaces/demo/cities/4.1/cities https://services.interactive-instruments.de/ogc-reference/schema/demo/cities/4.1/Cities.xsd http://www.opengis.net/wfs/2.0 https://services.interactive-instruments.de/ogc-reference/schema/ogc/wfs/2.0/wfs.xsd http://www.opengis.net/gml/3.2 https://services.interactive-instruments.de/ogc-reference/schema/ogc/gml/3.2.1/gml.xsd http://www.opengis.net/gml/3.2 https://services.interactive-instruments.de/ogc-reference/schema/ogc/gml/3.2.1/gml.xsd">
<wfs:boundedBy>
<gml:Envelope srsName="urn:ogc:def:crs:EPSG::25832" srsDimension="2">
<gml:lowerCorner>364883.268 5620035.394</gml:lowerCorner>
<gml:upperCorner>364883.268 5620035.394</gml:upperCorner>
</gml:Envelope>
</wfs:boundedBy>
<wfs:member>
<City gml:id="City.11">
<name>Bonn</name>
<location>
<gml:Point gml:id="city.id.11.location.Geom_0" srsName="urn:ogc:def:crs:EPSG::25832" srsDimension="2">
<gml:pos>364883.268 5620035.394</gml:pos>
</gml:Point>
</location>
<country>Germany</country>
<inhabitants>324800</inhabitants>
</City>
</wfs:member>
</wfs:FeatureCollection>
1.3.2 数据更新

此可选操作允许更新或删除要素实例及其属性。该操作还可用于插入新功能。WFS标准不强制实施任何特定的安全模型,因此实现应该实现适合于其自身基础设施的安全模型。

1.3.2.1 请求

下面显示了一个请求示例。由于请求修改数据,我们建议安装一个WFS的本地实例。下面的示例针对本地托管的WFS实例进行了测试<本地主机http://localhost:8080/geoserver/wfs>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<wfs:Transaction version="2.0.0" service="WFS"
xmlns="http://www.someserver.com/myns"
xmlns:fes="http://www.opengis.net/fes/2.0"
xmlns:topp="http://www.openplans.org/topp"
xmlns:wfs="http://www.opengis.net/wfs/2.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.opengis.net/wfs/2.0 http://schemas.opengis.net/wfs/2.0.0/wfs.xsd">
<wfs:Update typeName="topp:tasmania_roads">
<wfs:Property>
<wfs:ValueReference>TYPE</wfs:ValueReference>
<wfs:Value>road</wfs:Value>
</wfs:Property>
<fes:Filter>
<fes:ResourceId rid="tasmania_roads.1"/>
</fes:Filter>
</wfs:Update>
</wfs:Transaction>
1.3.2.2 响应

响应是一个XML文档,用于确认事务是否成功。

1
2
3
4
5
6
7
8
9
10
11
12
<wfs:TransactionResponse xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:fes="http://www.opengis.net/fes/2.0" xmlns:wfs="http://www.opengis.net/wfs/2.0" xmlns:gml="http://www.opengis.net/gml/3.2" xmlns:ows="http://www.opengis.net/ows/1.1" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.0.0" xsi:schemaLocation="http://www.opengis.net/wfs/2.0 http://localhost:8080/geoserver/schemas/wfs/2.0/wfs.xsd">
<wfs:TransactionSummary>
<wfs:totalInserted>0</wfs:totalInserted>
<wfs:totalUpdated>1</wfs:totalUpdated>
<wfs:totalReplaced>0</wfs:totalReplaced>
<wfs:totalDeleted>0</wfs:totalDeleted>
</wfs:TransactionSummary>
<wfs:UpdateResults><wfs:Feature>
<fes:ResourceId rid="tasmania_roads.1"/>
</wfs:Feature>
</wfs:UpdateResults>
</wfs:TransactionResponse>

1.4 城管原生 WFS 请求实例

GetFeature 不鉴权请求服务地址直接替换 layerName

1
2
3
4
5
6
7
const layerName = 'town_area_2'
const url =
`http://172.18.34.198:8000/geoserver/gis/ows?service=WFS&version=1.0.0&request=GetFeature&typeName=gis:${layerName}&maxFeatures=50&outputFormat=application/json`

fetch(url).then(res => {
console.log(res) // json 数据
})

1.5 基于 WFS 的增加、删除、修改、查询接口前端封装

1.5.1 接口说明

对用户输入的图层进行要素查询,返回 geojson

1.5.2 参数说明
参数 说明 类型 必须
ip 服务ip地址,城管委为172.18.34.198 字符串 是
port 服务端口号,城管委为8000 整数 是
workspace 工作空间,城管委为gis 字符串 是
layerName 图层名称,可登录城管委GIS系统->地图图层管理->物理图层管理获取需要的图层名 字符串 是
geomName 几何字段字段名,城管委数据为geom 字符串 是
maxFeatures 最大返回要素数量,不填全部返回,影响查询速度 整数 否
crs 返回数据坐标系,只需要提供code,默认EPSG:4326 字符串 否
filter 过滤条件 对象 否
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import { WFST } from "cci-map"
// import { WFST } from 'cci-wfs'
// 数据查询
let wfst = new WFST({
url: "http://{ip}:{port}/geoserver/{workspace}/wfs",
typeNS: "{workspace}",
typeName: "{layerName}",
geometryField: "{geomName}",
maxFeatures: 50,
crs: {
code: "EPSG:4326",
},
})
.on('load', res => {
console.log(res);
}).on('error', error => {
console.log(error);
})
1.5.3 filter 过滤器

OGC Filter Encoding v1.1.0 实现

名称 构造函数
ID
GmlObjectId GmlObjectIdFilter(value id)
Comparisons
PropertyIsEqualTo OperatorFilter.EQ(propertyExpression firstArgument, literalExpression secondArgument, bool matchCase)
PropertyIsNotEqualTo OperatorFilter.NotEQ(propertyExpression firstArgument, literalExpression secondArgument, bool matchCase)
PropertyIsLessThan OperatorFilter.LT(propertyExpression firstArgument, literalExpression secondArgument, bool matchCase)
PropertyIsGreaterThan OperatorFilter.GT(propertyExpression firstArgument, literalExpression secondArgument, bool matchCase)
PropertyIsLessThanOrEqualTo OperatorFilter.LEQ(propertyExpression firstArgument, literalExpression secondArgument, bool matchCase)
PropertyIsGreaterThanOrEqualTo OperatorFilter.GEQ(propertyExpression firstArgument, literalExpression secondArgument, bool matchCase)
PropertyIsLike LikeFilter(string propertyName,string likeExpression,object attributes)
PropertyIsNull IsNullFilter(string propertyName)
PropertyIsBetween IsBetweenFilter(propertyExpression firstArgument, literalExpression lowerBoundary, literalExpression upperBoundary)
Logic
And LogicFilter.And(expression[, expression]*)
Or LogicFilter.Or(expression[, expression]*)
Not LogicFilter.Not(filter)
Geojson
Geojson Geojson(geojson,ICRS fromCRS, ICRS toCRS)
Spatial
Equals SpatialFilter.Equals(string propertyName, Geojson geometry)
Disjoint SpatialFilter.Disjoint(string geomPropertyName, Geojson geometry)
Touches SpatialFilter.Touches(string geomPropertyName, Geojson geometry)
Within SpatialFilter.Within(string geomPropertyName, Geojson geometry)
Overlaps SpatialFilter.Overlaps(string geomPropertyName, Geojson geometry)
Crosses SpatialFilter.Crosses(string geomPropertyName, Geojson geometry)
Intersects SpatialFilter.Intersects(string geomPropertyName, Geojson geometry)
Contains SpatialFilter.Contains(string geomPropertyName, Geojson geometry)
Spatial distance buffer
DWithin DistanceBufferFilter.DWithin(string geomPropertyName, Geojson geometry, value distance, string units)
Beyond DistanceBufferFilter.Beyond(string geomPropertyName, Geojson geometry, value distance, string units)
1.5.4 例子
1.5.4.1 WFS 查询
  • PropertyIsEqualTo 字段值等于
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import { WFS, OperatorFilter } from 'cci-map'
// import { WFS, OperatorFilter } from 'cci-wfs'
// OBJECTID=9
// matchCase属性,其值为true或false。如果此属性是true(默认值),则字符串比较区分大小写
let filter = OperatorFilter.EQ("OBJECTID", "1109", false);
new WFS({
url: "http://{ip}:{port}/geoserver/{workspace}/wfs",
typeNS: "{workspace}",
typeName: "{layerName}",
geometryField: "{geomName}",
filter: filter,
maxFeatures: 50,
})
.on('load', res => {
console.log(res);
})
  • Intersects
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import { WFS, Geojson, SpatialFilter } from 'cci-map'
// import { WFS, Geojson, SpatialFilter } from 'cci-wfs'
// 几何筛选
const geom = {
type: "LineString",
coordinates: [
[120.03481352, 30.35337265],
[120.33481352, 30.35337265],
[120.33481352, 30.15337265],
[120.03481352, 30.15337265],
[120.03481352, 30.35337265],
],
};
let geommm = Geojson(geom);
let filter = SpatialFilter.Intersects(geommm, "{geomName}");
new WFS({
url: "http://{ip}:{port}/geoserver/{workspace}/wfs",
typeNS: "{workspace}",
typeName: "{layerName}",
geometryField: "{geomName}",
filter: filter,
maxFeatures: 50,
}).on('load', res => {
console.log(res);
})
  • And
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import { WFS, Geojson, LogicFilter, SpatialFilter, OperatorFilter } from 'cci-map' 
// import { WFS, Geojson, LogicFilter, SpatialFilter, OperatorFilter } from 'cci-wfs'
// and条件查询
let filter = OperatorFilter.EQ("OBJECTID", "1109");
const geom = {
type: "LineString",
coordinates: [
[120.03481352, 30.35337265],
[120.33481352, 30.35337265],
[120.33481352, 30.15337265],
[120.03481352, 30.15337265],
[120.03481352, 30.35337265],
],
};
let geommm = Geojson(geom);
let filter1 = SpatialFilter.Intersects(geommm, "{geomName}");
let finf = LogicFilter.And(filter, filter1);
new WFS({
url: "http://{ip}:{port}/geoserver/{workspace}/wfs",
typeNS: "{workspace}",
typeName: "{layerName}",
geometryField: "{geomName}",
filter: finf,
maxFeatures: 50,
}).on('load', res => {
console.log(res);
})
  • Like
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import { WFS, LikeFilter } from 'cci-map'
// import { WFS, LikeFilter } from 'cci-wfs'

// wildCard 指定匹配零个或多个字符串字符的任何序列的模式字符
// singleChar 指定匹配任何单个字符串字符的模式字符
// escapeChar 指定可用于转义模式字符的转义字符
// matchCase属性,其值为true或false。如果此属性是true(默认值),则字符串比较区分大小写
// like查询
const attributes = {
wildCard: '*',
singleChar: '#',
escapeChar: '!',
matchCase: true
}
let likeFilter = LikeFilter("网格编码", "*0707", attributes);
new WFS({
url: "http://{ip}:{port}/geoserver/{workspace}/wfs",
typeNS: "{workspace}",
typeName: "{layerName}",
geometryField: "{geomName}",
filter: likeFilter,
maxFeatures: 50,
}).on('load', res => {
console.log(res);
})
  • Between
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import { WFS, IsBetweenFilter } from 'cci-map'
// import { WFS, IsBetweenFilter } from 'cci-wfs'

// between查询
let isBettwen = IsBetweenFilter("AREA_1", 14675, 20000);
new WFS({
url: "http://{ip}:{port}/geoserver/{workspace}/wfs",
typeNS: "{workspace}",
typeName: "{layerName}",
geometryField: "{geomName}",
filter: isBettwen,
maxFeatures: 50,
}).on('load', res => {
console.log(res);
})
  • Not
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import { WFS, IsBetweenFilter, NotFilter } from 'cci-map'
// import { WFS, IsBetweenFilter, NotFilter } from 'cci-wfs'

// not查询
let notisBettwen = NotFilter(
IsBetweenFilter("AREA_1", 14675, 20000)
);
new WFS({
url: "http://{ip}:{port}/geoserver/{workspace}/wfs",
typeNS: "{workspace}",
typeName: "{layerName}",
geometryField: "{geomName}",
filter: notisBettwen,
maxFeatures: 50,
}).on('load', res => {
console.log(res);
})
  • IsNull
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import { WFS, IsNullFilter } from 'cci-map'
// import { WFS, IsNullFilter } from 'cci-wfs'

// isNull查询
let isNull = IsNullFilter(
'AREA'
);
new WFS({
url: "http://{ip}:{port}/geoserver/{workspace}/wfs",
typeNS: "{workspace}",
typeName: "{layerName}",
geometryField: "{geomName}",
filter: isNull,
maxFeatures: 50,
}).on('load', res => {
console.log(res);
})
1.5.4.2 WFS-T 在线编辑
  • 初始化
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import { WFST } from 'cci-map'
// import { WFST } from 'cci-wfs'
let wfst = new WFST({
url: "http://{ip}:{port}/geoserver/gis/wfs",
typeNS: "{workspaceName}",
typeName: "{layerName}",
geometryField: "{geomFieldName}",
maxFeatures: 50,
crs: {
code: "EPSG:4326",
},
}).on('load', res => {
// 查询结果
console.log(res);
})
  • 新增
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import { Geojson } from 'cci-map' 
// import { Geojson } from 'cci-wfs'
// 新增要素
const geom = {
type: "LineString",
coordinates: [
[120.03481352, 30.35337265],
[120.33481352, 30.35337265],
[120.33481352, 30.15337265],
[120.03481352, 30.15337265],
],
};
let geommm = Geojson(geom);
const layerAdd = {
id: 1, // 用于标识未保存的数据,未保存的数据里唯一
feature: { // 记录要素信息,主键id自动生成
properties: {},
geometry: geommm
}
}
// 完成操作时增加
wfst.addLayer(layerAdd)
// 保存提交数据
wfst.save();
  • 编辑
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import { Geojson } from 'cci-map' 
// import { Geojson } from 'cci-wfs'

// 编辑数据
const geomUpdate = {
type: "LineString",
coordinates: [
[120.03481352, 30.35337265],
[120.33481352, 30.35337265],
[120.33481352, 30.15337265]
],
};
let updateGeom = Geojson(geomUpdate);
const layerUpdate = {
id: 2,
feature: {
id: '{id}',
// 更新的属性
properties: {
propertyName: "test",
propertyName2: "test2",
},
// 更新的空间信息
geometry: updateGeom,
},
};
wfst.editLayer(layerUpdate);
wfst.save();
  • 删除
1
2
3
4
5
6
7
8
9
// 移除数据
const layerRemove = {
id: 2, // 用于标识未保存的数据,未保存的数据里唯一
feature: {
id: '{id}' // 数据中的id
},
};
wfst.removeLayer(layer);
wfst.save();

2. 搜索

2.1 根据地址描述搜索地址接口

2.1.1 接口说明

对用户输入的地名地址进行搜索,返回标准地址。

2.1.2 接口地址

http://172.18.34.198:8000/geocoding-web/geocode/getLocationByAddressV2

2.1.3 请求方式

GET/POST

2.1.4 参数说明
参数 说明 类型 必须
addr 待搜索的地址 字符串 是
page 结果分页参数,分页页码,默认 1 整数 否
limit 结果分页参数,每页结果数,默认 10 整数 否
where 地址类型查询条件参数, 查询楼栋: [{“key”:”address_type”,”logic”:0,”value”:1}] 查询户室: [{“key”:”address_type”,”logic”:0,”value”:0}] 查询室外地址: [{“key”:”address_type”,”logic”:5,”value”:1}] 查询非室外地址: [{“key”:”address_type”,”logic”:4,”value”:1}] 事件报送用: [{“key”:”address_type”,”logic”:8,”value”:[“0”,”1”,”61”,”62”,” 75”,”76”,”77”,”78”,”79”,”81”,”82”,”83”,”84”,”91”,”92”,”93 “,”94”,”95”,”96”,”97”,”98”]}] json 格式的 字符串 Post 传。 form-data 否
2.1.5 请求示例

http://172.18.34.198:8000/geocoding-web/geocode/getLocationByAddressV2?addr=天街&page=1&limit=10

2.1.6 返回示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
{
"code": 0,
"message": "操作成功",
"data": {
"code": 0,
"msg": "成功",
"success": true,
"data": {
"hits": 7758591,
"count": 10000,
"addrList": [
{
"door": null,
"loc": {
"coordinates": [
120.203829,
30.294988
],
"type": "Point"
},
"code": "330104012021004910017",
"city": "杭州市",
"county": "上城区",
"belong_building": null,
"lon": 120.203829,
"lv": 10,
"grid_code": "330102016018004",
"building_num": null,
"building": null,
"point": {
"lon": 120.203829,
"lat": 30.294988
},
"resregion": "红街天城",
"score": 0.45312497,
"szone": null,
"street": null,
"addr": "杭州市上城区笕桥街道天新社区红街天城",
"village": null,
"floor": null,
"lat": 30.294988,
"room_floor": null,
"town": "笕桥街道",
"address_type": 91,
"shape": null,
"community": "天新社区",
"room": null,
"is_delete": 0,
"unit": null,
"squad": null,
"datasource": "网格员采集",
"is_multi": 0
},
{
"door": null,
"loc": {
"coordinates": [
120.201323,
30.296952
],
"type": "Point"
},
"code": "330104012021003910015",
"city": "杭州市",
"county": "上城区",
"belong_building": null,
"lon": 120.201323,
"lv": 10,
"grid_code": "330102016018003",
"building_num": null,
"building": null,
"point": {
"lon": 120.201323,
"lat": 30.296952
},
"resregion": "红街公寓",
"score": 0.40010315,
"szone": null,
"street": null,
"addr": "杭州市上城区笕桥街道天新社区红街公寓",
"village": null,
"floor": null,
"lat": 30.296952,
"room_floor": null,
"town": "笕桥街道",
"address_type": 91,
"shape": null,
"community": "天新社区",
"room": null,
"is_delete": 0,
"unit": null,
"squad": null,
"datasource": "网格员采集",
"is_multi": 0
},
{
"door": null,
"loc": {
"coordinates": [
120.202346,
30.296307
],
"type": "Point"
},
"code": "330104012021002910013",
"city": "杭州市",
"county": "上城区",
"belong_building": null,
"lon": 120.202346,
"lv": 10,
"grid_code": "330102016018002",
"building_num": null,
"building": null,
"point": {
"lon": 120.202346,
"lat": 30.296307
},
"resregion": "红街公寓",
"score": 0.40010315,
"szone": null,
"street": null,
"addr": "杭州市上城区笕桥街道天新社区红街公寓",
"village": null,
"floor": null,
"lat": 30.296307,
"room_floor": null,
"town": "笕桥街道",
"address_type": 91,
"shape": null,
"community": "天新社区",
"room": null,
"is_delete": 0,
"unit": null,
"squad": null,
"datasource": "网格员采集",
"is_multi": 0
},
{
"door": null,
"loc": {
"coordinates": [
120.202134,
30.295621
],
"type": "Point"
},
"code": "330104012021001910011",
"city": "杭州市",
"county": "上城区",
"belong_building": null,
"lon": 120.202134,
"lv": 10,
"grid_code": "330102016018001",
"building_num": null,
"building": null,
"point": {
"lon": 120.202134,
"lat": 30.295621
},
"resregion": "红街公寓",
"score": 0.40010315,
"szone": null,
"street": null,
"addr": "杭州市上城区笕桥街道天新社区红街公寓",
"village": null,
"floor": null,
"lat": 30.295621,
"room_floor": null,
"town": "笕桥街道",
"address_type": 91,
"shape": null,
"community": "天新社区",
"room": null,
"is_delete": 0,
"unit": null,
"squad": null,
"datasource": "网格员采集",
"is_multi": 0
},
{
"door": null,
"loc": {
"coordinates": [
120.315471,
30.364963
],
"type": "Point"
},
"code": "330110002014003000118",
"city": "杭州市",
"county": "临平区",
"belong_building": null,
"lon": 120.315471,
"lv": 11,
"grid_code": "330113002012003",
"building_num": null,
"building": "梵天寺",
"point": {
"lon": 120.315471,
"lat": 30.364963
},
"resregion": null,
"score": 0.39240736,
"szone": null,
"street": "天小线",
"addr": "杭州市临平区南苑街道天万社区3组天小线梵天寺",
"village": null,
"floor": null,
"lat": 30.364963,
"room_floor": null,
"town": "南苑街道",
"address_type": 1,
"shape": null,
"community": "天万社区",
"room": null,
"is_delete": 0,
"unit": null,
"squad": "3组",
"datasource": "网格员采集",
"is_multi": 0
},
{
"door": null,
"loc": {
"coordinates": [
120.210785,
30.353536
],
"type": "Point"
},
"code": "33010401300300198048X",
"city": "杭州市",
"county": "上城区",
"belong_building": null,
"lon": 120.210785,
"lv": 10,
"grid_code": "330102017026001",
"building_num": null,
"building": null,
"point": {
"lon": 120.210785,
"lat": 30.353536
},
"resregion": "龙湖天街",
"score": 0.39240736,
"szone": null,
"street": null,
"addr": "杭州市上城区丁兰街道丁兰街道直属网格龙湖天街",
"village": null,
"floor": null,
"lat": 30.353536,
"room_floor": null,
"town": "丁兰街道",
"address_type": 98,
"shape": null,
"community": "丁兰街道直属网格",
"room": null,
"is_delete": 0,
"unit": null,
"squad": null,
"datasource": "网格员采集",
"is_multi": 0
},
{
"door": null,
"loc": {
"coordinates": [
120.209081,
30.353474
],
"type": "Point"
},
"code": "330104013003001980244",
"city": "杭州市",
"county": "上城区",
"belong_building": null,
"lon": 120.209081,
"lv": 11,
"grid_code": "330102017026001",
"building_num": null,
"building": "西门",
"point": {
"lon": 120.209081,
"lat": 30.353474
},
"resregion": "龙湖天街",
"score": 0.39240736,
"szone": null,
"street": null,
"addr": "杭州市上城区丁兰街道丁兰街道直属网格龙湖天街西门",
"village": null,
"floor": null,
"lat": 30.353474,
"room_floor": null,
"town": "丁兰街道",
"address_type": 98,
"shape": null,
"community": "丁兰街道直属网格",
"room": null,
"is_delete": 0,
"unit": null,
"squad": null,
"datasource": "网格员采集",
"is_multi": 0
},
{
"door": null,
"loc": {
"coordinates": [
120.211064,
30.354779
],
"type": "Point"
},
"code": "330104013003001980252",
"city": "杭州市",
"county": "上城区",
"belong_building": null,
"lon": 120.211064,
"lv": 11,
"grid_code": "330102017026001",
"building_num": null,
"building": "东门",
"point": {
"lon": 120.211064,
"lat": 30.354779
},
"resregion": "龙湖天街",
"score": 0.39240736,
"szone": null,
"street": null,
"addr": "杭州市上城区丁兰街道丁兰街道直属网格龙湖天街东门",
"village": null,
"floor": null,
"lat": 30.354779,
"room_floor": null,
"town": "丁兰街道",
"address_type": 98,
"shape": null,
"community": "丁兰街道直属网格",
"room": null,
"is_delete": 0,
"unit": null,
"squad": null,
"datasource": "网格员采集",
"is_multi": 0
},
{
"door": "318号",
"loc": {
"coordinates": [
120.204213,
30.294643
],
"type": "Point"
},
"code": "330104012021004000099",
"city": "杭州市",
"county": "上城区",
"belong_building": null,
"lon": 120.204213,
"lv": 12,
"grid_code": "330102016018004",
"building_num": "6幢",
"building": null,
"point": {
"lon": 120.204213,
"lat": 30.294643
},
"resregion": "红街天城",
"score": 0.39062497,
"szone": null,
"street": "新风路",
"addr": "杭州市上城区笕桥街道天新社区新风路318号红街天城6幢",
"village": null,
"floor": null,
"lat": 30.294643,
"room_floor": null,
"town": "笕桥街道",
"address_type": 1,
"shape": null,
"community": "天新社区",
"room": null,
"is_delete": 0,
"unit": null,
"squad": null,
"datasource": "网格员采集",
"is_multi": 0
},
{
"door": "318号",
"loc": {
"coordinates": [
120.203276,
30.294787
],
"type": "Point"
},
"code": "330104012021004000080",
"city": "杭州市",
"county": "上城区",
"belong_building": null,
"lon": 120.203276,
"lv": 12,
"grid_code": "330102016018004",
"building_num": "5幢",
"building": null,
"point": {
"lon": 120.203276,
"lat": 30.294787
},
"resregion": "红街天城",
"score": 0.39062497,
"szone": null,
"street": "新风路",
"addr": "杭州市上城区笕桥街道天新社区新风路318号红街天城5幢",
"village": null,
"floor": null,
"lat": 30.294787,
"room_floor": null,
"town": "笕桥街道",
"address_type": 1,
"shape": null,
"community": "天新社区",
"room": null,
"is_delete": 0,
"unit": null,
"squad": null,
"datasource": "网格员采集",
"is_multi": 0
}
]
}
},
"success": true
}
2.1.7 返回参数说明

2.2 通过坐标点获取地址描述信息

2.2.1 接口说明

根据坐标获取地址描述信息,通过输入的坐标,查询离输入坐标点最近的地址数据, 并返回输入坐标点相对于该条地址的坐标方位信息和相对距离。

2.2.2 接口地址

http://172.18.34.198:8000/geocoding-web/geocode/getLocationByPoint

2.2.3 请求方式

GET/POST

2.2.4 参数说明
参数 说明 类型 必须
lat 纬度 Double 是
lon 经度 Double 是
distance 查询范围的半径, 传参时需要指定距离 单位,不传默认为 1km,支持距离单位 m 和 km,如果想查询 500m 范围内的数 据,可以传 500m 或 0.5km String 否
addressType 地址类型:0 房屋,1 楼栋,不传查询 全部地址类型 String 否
page 分页页码, 默认为 1 Integer 否
size 每页大小, 默认为 1 Integer 否
2.2.5 请求示例

http://172.18.34.198:8000/geocoding-web/geocode/getLocationByPoint?lon=120&lat=30

2.2.6 返回示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
{
"code": 0,
"message": "操作成功",
"data": {
"code": 0,
"msg": "成功",
"success": true,
"data": [
{
"near_addr": "杭州市富阳区大源镇亭山村蝴蝶阁东门",
"is_overlap": false,
"rel_near_addr_position": "西南",
"addr_desc": {
"door": null,
"loc": {
"coordinates": [
120.0001177,
30.00012726
],
"type": "Point"
},
"code": "330111117243002980038",
"city": "杭州市",
"county": "富阳区",
"belong_building": null,
"check_type": null,
"lon": 120.0001177,
"lv": 11,
"grid_code": "330111117243002",
"addrReachCoordinatePosition": "西南",
"building_num": null,
"building": "东门",
"point": {
"lon": 120.0001177,
"lat": 30.00012726
},
"resregion": "蝴蝶阁",
"szone": null,
"usage_type": null,
"street": null,
"addr": "杭州市富阳区大源镇亭山村蝴蝶阁东门",
"village": null,
"floor": null,
"lat": 30.00012726,
"room_floor": null,
"town": "大源镇",
"address_type": "98",
"shape": null,
"community": "亭山村",
"room": null,
"unit": null,
"squad": null,
"datasource": "网格员采集",
"is_multi": "0"
},
"rel_near_addr_distance": 18
}
]
},
"success": true
}
2.2.7 返回参数说明

3. 路径规划

路径规划方法 pathPlan 内部调用高德服务接口,参数需要传入高德地图服务 token

3.1 请求参数说明

参数 说明 类型 必须
token 高德地图服务 token String 是
origin 起点火星坐标系经纬度字符串,用逗号拼接 String 是
destination 终点火星坐标系经纬度字符串,用逗号拼接 String 是
by 交通类型:walking/bicycling/electrobike/bus/driving 默认driving String 否
toCRS 指定高德路径数据转换经纬度坐标系,gcj02/wgs84/bd09, 默认 gcj02,也就是不做转换 String 否

3.2 示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
import { LineLayer, CMap, GaodeMap, pathPlan, polylineString2coords } from 'cci-map'
// import { CMap, GaodeMap } from 'cci-map'
// import { LineLayer } from 'cci-layers'
// import { pathPlan, polylineString2coords } from 'cci-utils'
const scene = new CMap({
id: 'map',
map: new GaodeMap({
center: [120.19382669582967, 30.258134],
pitch: 0,
zoom: 8,
viewMode: '3D'
})
})
scene.on('loaded', () => {
const token = '9795c67f0efaf73a3276c40ccf55a428'
const origin = '120,30'
const destination = '121,30'
pathPlan({
token, origin, destination
}).then(res => {
const multiLineString = []
res.route.paths[0].steps.forEach(path => {
multiLineString.push(polylineString2coords(path.polyline, ';'))
})
const layer = new LineLayer({})
.source({
type: 'FeatureCollection',
name: 'dl2',
crs: {
type: 'name',
properties: {
name: 'urn:ogc:def:crs:OGC:1.3:CRS84'
}
},
features: [
{
type: 'Feature',
properties: {},
geometry: {
type: 'MultiLineString',
coordinates: multiLineString
}
}
]
})
.size(1)
.shape('line')
.color('#25d8b7')
scene.addLayer(layer)
})
})

4. 路径纠偏

路径纠偏 graspPath 内部调用高德服务接口,参数需要传入高德地图 token

  • 纠偏之前需要按照下面的数据规格准备原始轨迹点,x、y、sp、ag、tm分别代表经度、纬度、速度、角度、时间。

  • 经纬度应该是高德坐标,而不是GPS直接采集的坐标

  • tm以秒为单位,第一个采集点的tm值从1970年0点开始,其他采集点为与第一个采集点时间的差值

4.1 请求参数说明

参数 说明 类型 必须
token 高德地图服务 token String 是
version 高德地图 api 版本,默认 2.0 String 否
path 火星坐标系经纬度数组 Array 是
toCRS 指定高德路径数据转换经纬度坐标系,gcj02/wgs84/bd09, 默认 gcj02,也就是不做转换 String 否

4.2 返回参数

  • distance 返回里程数
  • newPath 返回纠偏后对应坐标系经纬度数组

4.3 示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
import { Scene, MapTalks, graspPath, lineLayer } from 'cci-map'
// import { Scene, MapTalks } from 'cci-map'
// import { graspPath } from 'cci-utils'
// import { lineLayer } from 'cci-layers'

const scene = new Scene({
id: 'map',
map: new MapTalks({
center: [116.478928, 39.997761],
zoom: 16,
style: 'light'
})
})
scene.on('loaded', () => {
const path = [
{ "x": 116.478928, "y": 39.997761, "sp": 19, "ag": 0, "tm": 1478031031 },
{ "x": 116.478907, "y": 39.998422, "sp": 10, "ag": 0, "tm": 2 },
{ "x": 116.479384, "y": 39.998546, "sp": 10, "ag": 110, "tm": 3 },
{ "x": 116.481053, "y": 39.998204, "sp": 10, "ag": 120, "tm": 4 },
{ "x": 116.481793, "y": 39.997868, "sp": 10, "ag": 120, "tm": 5 },
{ "x": 116.482898, "y": 39.998217, "sp": 10, "ag": 30, "tm": 6 },
{ "x": 116.483789, "y": 39.999063, "sp": 10, "ag": 30, "tm": 7 },
{ "x": 116.484674, "y": 39.999844, "sp": 10, "ag": 30, "tm": 8 }
]

const formatPath = path.map(p => [p.x, p.y])

const oldLine = new LineLayer()
.source({
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"geometry": {
"type": "LineString",
"coordinates": formatPath
}
}
]
})
.color('red')
scene.addLayer(oldLine)

graspPath({
path,
toCRS: 'wgs84'
})
.then(({ newPath }) => {
newPath = newPath.map(p => [p.x, p.y])
const newLine = new LineLayer()
.source({
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"geometry": {
"type": "LineString",
"coordinates": newPath
}
}
]
})
.color('blue')
scene.addLayer(newLine)
})
})

5. 行政区划查询

行政区划查询 districtQuery 内部调用高德服务接口,参数需要传入高德地图服务 token

5.1 请求参数说明

参数名 含义 规则说明 是否必须 缺省值
token 请求服务权限标识 高德地图服务 token 必填 无
keywords 查询关键字 规则:只支持单个关键词语搜索关键词支持:行政区名称、citycode、adcode例如,在subdistrict=2,搜索省份(例如山东),能够显示市(例如济南),区(例如历下区)adcode信息可参考城市编码表获取 可选 无
subdistrict 子级行政区 规则:设置显示下级行政区级数(行政区级别包括:国家、省/直辖市、市、区/县、乡镇/街道多级数据)可选值:0、1、2、3等数字,并以此类推0:不返回下级行政区;1:返回下一级行政区;2:返回下两级行政区;3:返回下三级行政区; 需要在此特殊说明,目前部分城市和省直辖县因为没有区县的概念,故在市级下方直接显示街道。例如:广东-东莞、海南-文昌市 可选 1
page 需要第几页数据 最外层的districts最多会返回20个数据,若超过限制,请用page请求下一页数据。例如page=2;page=3。默认page=1 可选 1
offset 最外层返回数据个数 可选 20
extensions 返回结果控制 此项控制行政区信息中返回行政区边界坐标点; 可选值:base、all;base:不返回行政区边界坐标点;all:只返回当前查询district的边界值,不返回子节点的边界值;目前不能返回乡镇/街道级别的边界值 可选 base
filter 根据区划过滤 按照指定行政区划进行过滤,填入后则只返回该省/直辖市信息需填入adcode,为了保证数据的正确,强烈建议填入此参数 可选
callback 回调函数 callback值是用户定义的函数名称,此参数只在output=JSON时有效 可选
output 返回数据格式类型 可选值:JSON,XML 可选 JSON
toCRS 返回数据坐标系 指定高德行政区划查询数据转换经纬度坐标系,gcj02/wgs84/bd09, 默认 gcj02,也就是不做转换 可选 gcj02

5.2 返回参数

名称 含义 规则说明
status 返回结果状态值 值为0或1,0表示失败;1表示成功
info 返回状态说明 返回状态说明,status为0时,info返回错误原因,否则返回“OK”。
infocode 状态码 返回状态说明,10000代表正确,详情参阅info状态表
suggestion 建议结果列表
keywords 建议关键字列表
cites 建议城市列表
districts 行政区列表
district 行政区信息
citycode 城市编码
adcode 区域编码 街道没有独有的adcode,均继承父类(区县)的adcode
name 行政区名称
polyline 行政区边界坐标点 当一个行政区范围,由完全分隔两块或者多块的地块组成,每块地的 polyline 坐标串以 \ 分隔 。如北京 的 朝阳区
center 区域中心点
level 行政区划级别 country:国家province:省份(直辖市会在province和city显示)city:市(直辖市会在province和city显示)district:区县street:街道
districts 下级行政区列表,包含district元素

5.3 示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import { CMap, GaodeMap, districtQuery } from 'cci-map'
// import { CMap, GaodeMap } from 'cci-map'
// import { districtQuery } from 'cci-utils'

const scene = new CMap({
id: 'map',
map: new GaodeMap({
center: [120.19382669582967, 30.258134],
pitch: 0,
zoom: 8,
viewMode: '3D'
})
})
scene.on('loaded', () => {
const token = '9795c67f0efaf73a3276c40ccf55a428'
districtQuery({
token,
// toCRS: 'wgs84',
keywords: '山东',
subdistrict: 3
}).then(res => {
console.log(res, 'res:::::::::::::')
})
})

6. 天气查询

天气查询 weatherQuery 内部调用高德服务接口,参数需要传入高德地图服务 token

6.1 请求参数说明

参数名 含义 规则说明 是否必须 缺省值
token 请求服务权限标识 高德地图服务 token 必填 无
city 城市编码 输入城市的adcode,adcode信息可参考城市编码表 必填 无
extensions 气象类型 可选值:base/allbase:返回实况天气 all:返回预报天气 可选 无
output 返回格式 可选值:JSON,XML 可选 JSON

6.2 返回参数说明

实况天气每小时更新多次,预报天气每天更新3次,分别在8、11、18点左右更新。由于天气数据的特殊性以及数据更新的持续性,无法确定精确的更新时间,请以接口返回数据的reporttime字段为准。天气结果对照表>>

名称 含义 规则说明
status 返回状态 值为0或11:成功;0:失败
count 返回结果总数目
info 返回的状态信息
infocode 返回状态说明,10000代表正确
lives 实况天气数据信息
province 省份名
city 城市名
adcode 区域编码
weather 天气现象(汉字描述)
temperature 实时气温,单位:摄氏度
winddirection 风向描述
windpower 风力级别,单位:级
humidity 空气湿度
reporttime 数据发布的时间
forecast 预报天气信息数据
city 城市名称
adcode 城市编码
province 省份名称
reporttime 预报发布时间
casts 预报数据list结构,元素cast,按顺序为当天、第二天、第三天的预报数据
date 日期
week 星期几
dayweather 白天天气现象
nightweather 晚上天气现象
daytemp 白天温度
nighttemp 晚上温度
daywind 白天风向
nightwind 晚上风向
daypower 白天风力
nightpower 晚上风力

6.3 示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import { CMap, GaodeMap, weatherQuery } from 'cci-map'
// import { CMap, GaodeMap } from 'cci-map'
// import { weatherQuery } from 'cci-utils'

const scene = new CMap({
id: 'map',
map: new GaodeMap({
center: [120.19382669582967, 30.258134],
pitch: 0,
zoom: 8,
viewMode: '3D'
})
})
scene.on('loaded', () => {
const token = '9795c67f0efaf73a3276c40ccf55a428'
weatherQuery({
token,
city: '110101'
}).then(res => {
console.log(res, 'res:::::::::::::')
})
})

7. ip 查询

ip查询 ipQuery 内部调用高德服务接口,参数需要传入高德地图服务 token

7.1 请求参数说明

参数名 含义 规则 是否必填
token 请求服务权限标识 高德地图服务 token 是
type IP类型 值为 4 或 6,4 表示 IPv4,6 表示 IPv6 是
ip 要查询的ip地址 如:221.206.131.10 是
sig 数字签名 数字签名认证用户必填 否
toCRS 返回数据坐标系 指定IP查询数据转换经纬度坐标系,gcj02/wgs84/bd09, 默认 gcj02,也就是不做转换 否

7.2 返回参数说明

名称 含义 规则
status 状态码 返回结果状态值,值为0或1,0表示失败;1表示成功
info 返回状态说明 status为0时,info返回错误原因;否则返回“OK”。详情参阅info状态
infocode 状态码 返回状态说明,10000代表正确,详情参阅info状态表
country 国家 国家(或地区),中文
province 省份 省(二级),中文
city 城市 市(三级),中文
district 区县 区(四级),中文
isp 运营商 如电信、联通、移动
location 经纬度 如 116.480881,39.989410
Ip IP地址 提交的 Ipv4/Ipv6 地址

7.3 示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import { CMap, GaodeMap, ipQuery } from 'cci-map'
// import { CMap, GaodeMap } from 'cci-map'
// import { ipQuery } from 'cci-utils'

const scene = new CMap({
id: 'map',
map: new GaodeMap({
center: [120.19382669582967, 30.258134],
pitch: 0,
zoom: 8,
viewMode: '3D'
})
})
scene.on('loaded', () => {
const token = '9795c67f0efaf73a3276c40ccf55a428'
ipQuery({
token,
//toCRS: 'wgs84',
type: 4,
ip: '114.247.50.2'
}).then(res => {
console.log(res, 'res:::::::::::::')
})
})

8 地理/逆地理编码

地理编码/逆地理编码 API 是通过 HTTP/HTTPS 协议访问远程服务的接口,提供结构化地址与经纬度之间的相互转化的能力

8.1 地理编码

地理编码 geocodeQuery 内部封装高德服务接口,需要提供高德地图服务 token

8.1.1 请求参数说明
参数名 含义 规则说明 是否必须 缺省值
token 请求权限服务标志 高德地图服务 token 必填 无
address 结构化地址信息 规则遵循:国家、省份、城市、区县、城镇、乡村、街道、门牌号码、屋邨、大厦,如:北京市朝阳区阜通东大街6号。如果需要解析多个地址的话,请用”\ “进行间隔,并且将 batch 参数设置为 true,最多支持 10 个地址进进行”\ “分割形式的请求。 必填 无
city 指定查询的城市 可选输入内容包括:指定城市的中文(如北京)、指定城市的中文全拼(beijing)、citycode(010)、adcode(110000),不支持县级市。当指定城市查询内容为空时,会进行全国范围内的地址转换检索。adcode信息可参考城市编码表获取 可选 无,会进行全国范围内搜索
batch 批量查询控制 batch 参数设置为 true 时进行批量查询操作,最多支持 10 个地址进行批量查询。batch 参数设置为 false 时进行单点查询,此时即使传入多个地址也只返回第一个地址的解析查询结果。 可选 false
sig 数字签名 请参考数字签名获取和使用方法 可选 无
output 返回数据格式类型 可选输入内容包括:JSON,XML。设置 JSON 返回结果数据将会以JSON结构构成;如果设置 XML 返回结果数据将以 XML 结构构成。 可选 JSON
callback 回调函数 callback 值是用户定义的函数名称,此参数只在 output 参数设置为 JSON 时有效。 可选 无
toCRS 返回数据坐标系 指定地理编码数据转换经纬度坐标系,gcj02/wgs84/bd09 可选 gcj02
8.1.2 返回参数说明
名称 含义 规则说明
status 返回结果状态值 返回值为 0 或 1,0 表示请求失败;1 表示请求成功。
count 返回结果数目 返回结果的个数。
info 返回状态说明 当 status 为 0 时,info 会返回具体错误原因,否则返回“OK”。详情可以参阅info状态表
geocodes 地理编码信息列表 结果对象列表,包括下述字段:
formatted_address 结构化地址信息 省份+城市+区县+城镇+乡村+街道+门牌号码
country 国家 国内地址默认返回中国
province 地址所在的省份名 例如:北京市。此处需要注意的是,中国的四大直辖市也算作省级单位。
city 地址所在的城市名 例如:北京市
citycode 城市编码 例如:010
district 地址所在的区 例如:朝阳区
street 街道 例如:阜通东大街
number 门牌 例如:6号
adcode 区域编码 例如:110101
location 坐标点 经度,纬度
level 匹配级别 参见下方的地理编码匹配级别列表
8.1.3 示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import { CMap, GaodeMap, geocodeQuery } from 'cci-map'
// import { CMap, GaodeMap } from 'cci-map'
// import { geocodeQuery } from 'cci-utils'

const scene = new CMap({
id: 'map',
map: new GaodeMap({
center: [120.19382669582967, 30.258134],
pitch: 0,
zoom: 8,
viewMode: '3D'
})
})
scene.on('loaded', () => {
const token = '9795c67f0efaf73a3276c40ccf55a428'
geocodeQuery({
token,
toCRS: 'wgs84',
address: '北京市朝阳区阜通东大街6号',
city: '北京'
}).then(res => {
console.log(res, 'res:::::::::::::')
})
})

8.2 逆地理编码

地理编码 regeocodeQuery 内部封装高德服务接口,需要提供高德地图服务 token

8.2.1 请求参数说明
参数名 含义 规则说明 是否必须 缺省值
key 请求服务权限标志 高德地图服务 token 必填 无
location 经纬度坐标 传入内容规则:经度在前,纬度在后,经纬度间以“,”分割,经纬度小数点后不要超过 6 位。如果需要解析多个经纬度的话,请用”\ “进行间隔,并且将 batch 参数设置为 true,最多支持传入 20 对坐标点。每对点坐标之间用”\ “分割。默认是高德坐标系坐标 必填 无
poitype 返回附近POI类型 以下内容需要 extensions 参数为 all 时才生效。逆地理编码在进行坐标解析之后不仅可以返回地址描述,也可以返回经纬度附近符合限定要求的POI内容(在 extensions 字段值为 all 时才会返回POI内容)。设置 POI 类型参数相当于为上述操作限定要求。参数仅支持传入POI TYPECODE,可以传入多个POI TYPECODE,相互之间用“\ ”分隔。该参数在 batch 取值为 true 时不生效。获取 POI TYPECODE 可以参考POI分类码表 可选 无
radius 搜索半径 radius取值范围在0~3000,默认是1000。单位:米 可选 1000
extensions 返回结果控制 extensions 参数默认取值是 base,也就是返回基本地址信息;extensions 参数取值为 all 时会返回基本地址信息、附近 POI 内容、道路信息以及道路交叉口信息。 可选 base
batch 批量查询控制 batch 参数设置为 true 时进行批量查询操作,最多支持 20 个经纬度点进行批量地址查询操作。batch 参数设置为 false 时进行单点查询,此时即使传入多个经纬度也只返回第一个经纬度的地址解析查询结果。 可选 false
roadlevel 道路等级 以下内容需要 extensions 参数为 all 时才生效。可选值:0,1 当roadlevel=0时,显示所有道路 当roadlevel=1时,过滤非主干道路,仅输出主干道路数据 可选 无
sig 数字签名 请参考数字签名获取和使用方法 可选 无
output 返回数据格式类型 可选输入内容包括:JSON,XML。设置 JSON 返回结果数据将会以JSON结构构成;如果设置 XML 返回结果数据将以 XML 结构构成。 可选 JSON
callback 回调函数 callback 值是用户定义的函数名称,此参数只在 output 参数设置为 JSON 时有效。 可选 无
homeorcorp 是否优化POI返回顺序 以下内容需要 extensions 参数为 all 时才生效。homeorcorp 参数的设置可以影响召回 POI 内容的排序策略,目前提供三个可选参数:0:不对召回的排序策略进行干扰。1:综合大数据分析将居家相关的 POI 内容优先返回,即优化返回结果中 pois 字段的poi顺序。2:综合大数据分析将公司相关的 POI 内容优先返回,即优化返回结果中 pois 字段的poi顺序。 可选 0
fromCRS 请求数据坐标系 指定逆地理编码请求数据经纬度坐标系,gcj02/wgs84/bd09 可选 gcj02
toCRS 返回数据坐标系统 指定逆地理编码返回数据经纬度坐标系,gcj02/wgs84/bd09 可选 gcj0
8.2.2 返回参数说明
名称 含义 规则说明
status 返回结果状态值 返回值为 0 或 1,0 表示请求失败;1 表示请求成功。
info 返回状态说明 当 status 为 0 时,info 会返回具体错误原因,否则返回“OK”。详情可以参考info状态表
regeocode 逆地理编码列表 batch 字段设置为 true 时为批量请求,此时 regeocodes 标签返回,标签下为 regeocode 对象列表;batch 为false 时为单个请求,会返回 regeocode 对象;regeocode 对象包含的数据如下:
formatted_address 结构化地址信息 结构化地址信息包括:省份+城市+区县+城镇+乡村+街道+门牌号码如果坐标点处于海域范围内,则结构化地址信息为:省份+城市+区县+海域信息
addressComponent 地址元素列表
province 坐标点所在省名称 例如:北京市
city 坐标点所在城市名称 请注意:当城市是省直辖县时返回为空,以及城市为北京、上海、天津、重庆四个直辖市时,该字段返回为空;省直辖县列表地址元素列表
citycode 城市编码 例如:010
district 坐标点所在区 例如:海淀区
adcode 行政区编码 例如:110108
township 坐标点所在乡镇/街道(此街道为社区街道,不是道路信息) 例如:燕园街道
towncode 乡镇街道编码 例如:110101001000
neighborhood 社区信息列表
name 社区名称 例如:北京大学
type POI类型 例如:科教文化服务;学校;高等院校
building 楼信息列表
name 建筑名称 例如:万达广场
type 类型 例如:科教文化服务;学校;高等院校
streetNumber 门牌信息列表
street 街道名称 例如:中关村北二条
number 门牌号 例如:3号
location 坐标点 经纬度坐标点:经度,纬度
direction 方向 坐标点所处街道方位
distance 门牌地址到请求坐标的距离 单位:米
seaArea 所属海域信息 例如:渤海
businessAreas 经纬度所属商圈列表
businessArea 商圈信息
location 商圈中心点经纬度
name 商圈名称 例如:颐和园
id 商圈所在区域的adcode 例如:朝阳区/海淀区
roads 道路信息列表 请求参数 extensions 为 all 时返回如下内容
road 道路信息
id 道路id
name 道路名称
distance 道路到请求坐标的距离 单位:米
direction 方位 输入点和此路的相对方位
location 坐标点
roadinters 道路交叉口列表 请求参数 extensions 为 all 时返回如下内容
roadinter 道路交叉口
distance 交叉路口到请求坐标的距离 单位:米
direction 方位 输入点相对路口的方位
location 路口经纬度
first_id 第一条道路id
first_name 第一条道路名称
second_id 第二条道路id
second_name 第二条道路名称
pois poi信息列表 请求参数 extensions 为 all 时返回如下内容
poi poi信息
id poi的id
name poi点名称
type poi类型
tel 电话
distance 该POI的中心点到请求坐标的距离 单位:米
direction 方向 为输入点相对建筑物的方位
address poi地址信息
location 坐标点
businessarea poi所在商圈名称
aois aoi信息列表 请求参数 extensions 为 all 时返回如下内容
aoi aoi信息
id 所属 aoi的id
name 所属 aoi 名称
adcode 所属 aoi 所在区域编码
location 所属 aoi 中心点坐标
area 所属aoi点面积 单位:平方米
distance 输入经纬度是否在aoi面之中 0,代表在aoi内其余整数代表距离AOI的距离
8.2.3 示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import { CMap, GaodeMap, regeocodeQuery } from 'cci-map'
// import { CMap, GaodeMap } from 'cci-map'
// import { regeocodeQuery } from 'cci-utils'

const scene = new CMap({
id: 'map',
map: new GaodeMap({
center: [120.19382669582967, 30.258134],
pitch: 0,
zoom: 8,
viewMode: '3D'
})
})
scene.on('loaded', () => {
const token = '9795c67f0efaf73a3276c40ccf55a428'
regeocodeQuery({
token,
fromCRS: 'wgs84',
toCRS: 'wgs84',
location: '116.481488,39.990464|115,39.990464',
poitype: '商务写字楼',
roadlevel: 0
}).then(res => {
console.log(res, 'res:::::::::::::')
})
})

九、小程序

1. 教程

1.1 简介

通过引入 Mini 模块,能让用户在小程序环境中使用 地图可视化的能力,增强原生地图组件的可视化能力。

✨ 目前 Mini 兼容支付宝小程序,尚不支持微信小程序。
✨ 目前 Mini 只支持无底图模式,即只展示可视化层。
✨ Mini 模块的使用,除了画布获取和和事件注册因为小程序环境的原因需要用户额外处理,其余部分和普通 cci-map 使用保持一致。

下面将介绍如何在支付宝小程序中使用 Mini 模块。

1.2 安装

目前在小程序开发中使用的能力全部来自 Mini 模块,用户只需要执行一次安装即可。

1
npm install cci-mini --save

1.3 地图引用

在小程序环境中,用户无法引用高德地图和 Mapbox 地图, 只能引用小程序版本的地图类型。
在 .ts/.js 页面脚本文件中引用

1
2
3
4
5
6
7
8
import {
Map, // 其他地图类型不兼容小程序环境
Scene,
PointLayer,
dispatchTouchStart,
dispatchTouchMove,
dispatchTouchEnd,
} from 'cci-mini'

地图小程序环境的使用和普通 H5 环境的使用保持一致。

1
2
3
4
5
const miniMap = new Map({
center: [0, 0],
zoom: -0.5,
pitch: 0,
});

1.4 节点注册

由于小程序限制无法动态创建新的节点,因此需要事先在 .axml 文件中创建 canvas 画布节点

1
<canvas onReady="onCanvasReady" type="webgl" id="canvas" />

注册完的节点会在脚本文件中获取使用。

✨ 需要完成 onCanvasReady 事件的注册,以便明确获取节点的时机。

1.5 事件注册

由于小程序环境的限制无法动态注册事件,所以需要用户自己完成事件代理,下面将会说明如何完成事件的代理。

✨ 如果不进行事件的注册和转发,用户将无法对地图进行操作。

  • 在 .axml 文件中绑定基础事件

✨ 事件需要绑定在 canvas 节点上或是 canvas 的父节点上

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<view id="box" class="wrap"
onTouchStart="onTouchStart" // 绑定基础事件
onTouchMove="onTouchMove"
onTouchEnd="onTouchEnd"
>
<canvas onReady="onCanvasReady" type="webgl" id="canvas" />
<view class="populationWrap">
<view class="populationIcons">
人口/千万:
</view>
<view class="populationIcons" a:for="{{population}}">
<view> {{item.count}} </view>
<view class="colorLine" style="background:{{item.color}}"></view>
</view>
</view>
</view>
  • .ts/.js 文件中完成事件的代理转发

引入代理方法

1
2
3
4
5
6
7
8
import {
Map,
Scene,
PointLayer,
dispatchTouchStart, // Mini 封装的代理方法
dispatchTouchMove,
dispatchTouchEnd,
} from 'cci-mini'

在 page 对象中注册方法

1
2
3
4
5
6
7
8
9
10
11
12
13
page({
...
onTouchStart(e) {
dispatchTouchStart(e);
},
onTouchMove(e) {
dispatchTouchMove(e);
},
onTouchEnd(e) {
dispatchTouchEnd(e);
},
...
})

只需要完成基础方法的注册(touchstart/touchmove/touchend),Mini 会完成复合方法、手势的判断。

1.6 画布的获取与使用

在小程序中,需要使用小程序提供的方法来获取页面节点。

通过在 .axml 在 canvas 画布组件上注册的 onCanvasReady 方法来判断获取画布的时机。

1
2
3
4
5
6
7
8
9
10
11
page({
...
onCanvasReady() {
handleCanvas(my, canvas => {
...
// 正常开发 代码
...
})
}
...
})

下面提供了获取画布节点通用方法,同时对画布进行一些处理方便后续使用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
function handleCanvas(my, callback) {
const selector = my.createSelectorQuery();
const domSelector = selector.select('#canvas');
domSelector
.fields(
{
node: true,
context: false,
rect: true,
computedStyle: ['height', 'width'],
},
function(res) {
// 内部计算使用 (必须设置)
res.node.left = res.left;
res.node.top = res.top;

// 设置画布的 DPR (必须设置)
const DPR = my.getSystemInfoSync().pixelRatio;
res.node.width *= DPR;
res.node.height *= DPR;

// 返回 canvas 画布节点
callback(res.node);
},
)
.exec();
}

在获取到画布对象后需要传递给 scene 使用。

1
2
3
4
5
const miniScene = new Scene({
id: 'canvas',
canvas, // canvas 是我们从小程序页面上获取到的实际节点
map: miniMap,
});

1.7 销毁

为了保证小程序的良好体验,在页面关闭时记得及时将地图内容进行销毁。

1
2
3
4
5
6
7
8
9
10
page({
...
onHide() {
miniScene.destroy();
},
onUnload() {
miniScene.destroy();
},
...
})

1.8 限制

目前 Mini 尚不支持 marker/popup 等需要动态创建页面节点的能力,用户若是需要可以自己单独创建。

2. 3D 柱状实例

  • index.axml 页面结构代码
1
2
3
4
5
6
7
8
9
10
11
12
<view class="isLoading" style="height: 100vh" a:if="{{isLoading}}">
<view class="loadItem" a:for="{{10}}">
</view>
</view>

<view id="box" class="wrap"
onTouchStart="onTouchStart"
onTouchMove="onTouchMove"
onTouchEnd="onTouchEnd"
>
<canvas onReady="onCanvasReady" type="webgl" id="canvas" />
</view>
  • index.less 样式代码
1
2
3
4
5
6
7
.wrap {
height: 100vh;
#canvas {
height: 100%;
width: 100%;
}
}
  • index.js 脚本代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
import {
Map,
CMap,
PointLayer,
dispatchTouchStart,
dispatchTouchMove,
dispatchTouchEnd
} from 'cci-mini'
import { getJSON, handleCanvas, LayerCounter } from '../../utils';
const hexagonData = 'https://gw.alipayobjects.com/os/basement_prod/893d1d5f-11d9-45f3-8322-ee9140d288ae.json'

let miniScene;
let counter;

Page({
data: {
isLoading: true
},
onLoad() {
counter = new LayerCounter(1, my, this);
my.showLoading();
},
onTouchStart(e) {
dispatchTouchStart(e);
},
onTouchMove(e) {
dispatchTouchMove(e);
},
onTouchEnd(e) {
dispatchTouchEnd(e);
},
onCanvasReady() {
handleCanvas(this, my, (canvas) => {
const miniMap = new Map({
pitch: 60,
style: 'light',
center: [ 121.412224, 31.26192438 ],
zoom: 13.13438,
rotation: 35.97133
});
miniScene = new CMap({
id: 'canvas',
canvas,
map: miniMap,
hasBaseMap: false,
});
miniScene.setBgColor('rgb(240, 243, 246)')

getJSON(hexagonData, function (result, data) {
if (result) {
const layer = new PointLayer({})
.source(data, {
parser: {
type: 'json',
x: 'longitude',
y: 'latitude'
}
})
.shape('name', [
'cylinder',
'triangleColumn',
'hexagonColumn',
'squareColumn'
])
.animate(true)
.active(true)
.size('unit_price', h => {
return [ 6, 6, h / 500 ];
})
.color('name', [ '#5B8FF9', '#70E3B5', '#FFD458', '#FF7C6A' ])
.style({
opacity: 1.0
});

miniScene.addLayer(layer);
counter.loadLayer()
}
});

miniScene.on('loaded', function () {
counter.loadLayer();
});
});
},
onUnload() {
// 页面被关闭
miniScene.destroy();
},
});
  • 方法代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
export function getJSON(url, callback) {
my.request({
url,
method: 'GET',
data: {
from: '支付宝',
production: 'AlipayJSAPI',
},
headers: {
'content-type': 'application/json', // 默认值
},
dataType: 'json',
fail() {
callback(false, null);
},
complete(res) {
callback(true, res.data);
},
});
}

export function handleCanvas(_that, my, callback) {
const selector = my.createSelectorQuery();
const domSelector = selector.select('#canvas');
domSelector
.fields(
{
node: true,
context: false,
rect: true,
computedStyle: ['height', 'width'],
},
function (res) {
res.node.left = res.left;
res.node.top = res.top;

const DPR = my.getSystemInfoSync().pixelRatio;
res.node.width *= DPR;
res.node.height *= DPR;

callback(res.node);
},
)
.exec();
}

export class LayerCounter {
constructor(totalLayers, my, context) {
this.loadedLayer = 0
this.totalLayers = totalLayers;
this.my = my;
this.context = context;
}

loadLayer() {
this.loadedLayer++;
const that = this;
if (this.loadedLayer >= this.totalLayers) {
this.my.hideLoading({
page: that, // 防止执行时已经切换到其它页面,page 指向不准确
});
this.context.setData({
isLoading: false,
});
}
}
}

npm publish 发布自己的模块

发表于 2021-12-10

在 npm 官网上发布自己的模块首先需要在 https://www.npmjs.com 注册账号

1 编写模块

一般遵循 umd / amd / commonJS 风格编写模块

2 初始化包描述文件 package.json

使用 npm init 命令初始化包描述文件,要注意两点:

  • 包名不能包含大写字母
  • 最好先在 npm 官网搜索一下包名是否已被占用,被占用的包名模块是无法发布的
1
npm init
阅读全文 »

koa2 源码解读

发表于 2021-12-10

koa 是由 Express 原班人马打造的,致力于成为一个更小、更富有表现力、更健壮的 Web 框架。 使用 koa 编写 web 应用,通过组合不同的 generator(koa2 用 async/await),可以免除重复繁琐的回调函数嵌套, 并极大地提升错误处理的效率。koa 不在内核方法中绑定任何中间件, 它仅仅提供了一个轻量优雅的函数库,使得编写 Web 应用变得得心应手

基础

1
2
3
4
5
6
7
8
9
10
11
const http = require('http')

http.createServer(function(req, res, next) {
// res.writeHead(statusCode, [reason], [headers])
res.writeHead(200, {'Content-Type': 'text/html;charset=UTF-8'})
// res.statusCode = 200
// res.setHead('Content-Type', 'text/html;charset=UTF-8')
res.end('hi')
}).listen(3000, function() {
console.log('listen at port 3000')
})

使用 Node.js 原生创建一个 http 服务器的代码, 实际上是调用 http.createServer(requestListener), 创建一个 http.Server 的实例,http.Server 继承于 EventEmitter 类。传入的 requestListener 是一个函数,会自动添加到 ‘request’ 事件,最后将 http.Server 的实例绑定到 3000 端口,开始监听来自 3000 端口的请求

requestListener 函数接受两个参数 req 和 res,req 是一个可读流,res 是一个可写流。通过 req 获取该 http 请求的所有信息,同时将数据写入 res 来对请求做出响应

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// Node.js 源码 http.js
const server = require('_http_server');
const { Server } = server;
function createServer(requestListener) {
return new Server(requestListener);
}

// Node.js 源码 _http_server.js
// Server 构造函数 传入 requestListener,如果 requestListener 存在会被添加到 'request' 事件
function Server(requestListener) {
if (!(this instanceof Server)) return new Server(requestListener);
net.Server.call(this, { allowHalfOpen: true });

if (requestListener) {
this.on('request', requestListener);
}
...
}

常见中间件操作

  • 静态服务

    1
    app.use(require('koa-static')(__dirname + '/'))
  • 路由

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    const router = require('koa-router')()
    router.get('/string', async (ctx, next) => {
    ctx.body = 'koa2 string'
    })
    router.get('/json', async (ctx, next) => {
    ctx.body = {
    title: 'koa2 json'
    }
    })
    app.use(router.routes())
  • 日志

    1
    2
    3
    4
    5
    6
    app.use(async (ctx,next) => {
    const start = new Date().getTime()
    console.log(`start: ${ctx.url}`);
    await next();
    const end = new Date().getTime() console.log(`请求${ctx.url}, 耗时${parseInt(end-start)}ms`)
    })

源码

代码结构

1
2
3
4
5
- lib/
-- application.js // 入口文件
-- context.js
-- request.js
-- response.js
阅读全文 »

未命名

发表于 2020-02-28

dll

  1. dllPlugin 生成 require 或 inport 依赖 生成对应 id 映射 json
  2. dllReference 根据 json 生成 js

  3. 1 dllEntryPlugin 处理 vendor 依赖数组 LibManifestPlugin 生成 json

未命名

发表于 2019-12-30

title: 轮播图组件
tag:

- 轮播图

未命名

发表于 2019-12-22

搭建 npm 私服原因

  • 统一管理(公司内部开发的私有包,方便统一管理、开发和使用)
  • 安全性(由于公司内部开发的模块等并不希望公开, 但又希望内部能方便使用)
  • 加速(自建服务器自带常用 package 的缓存,会缓存下载过的包,节省时间)

使用verdaccio

verdaccio 是 sinopia 开源框架的一个fork,由于sinopia 已不再维护,因此采用 verdaccio

安装

1
npm i –g verdaccio

运行

1
2
3
4
5
6
7
verdaccio

warn --- config file - /home/admin/.config/verdaccio/config.yaml # 默认的配置文件
warn --- Verdaccio started
warn --- Plugin successfully loaded: verdaccio-htpasswd
warn --- Plugin successfully loaded: verdaccio-audit
warn --- http address - http://localhost:4873/ - verdaccio/4.4.0 # 默认端口

可以使用 pm2 来管理进程

1
2
3
npm i -g pm2
pm2 start verdaccio
# pm2 ls 查看进程情况

腾讯云服务器配置 nginx

verdaccio 默认是启动在 4873 端口,腾讯云服务器需要配置 nginx 反向代理到该端口,nginx 配置需要在 /etc/nginx/conf.d/ 下新建文件

1
2
3
4
5
6
7
8
server {
listen 80;
server_name registry.npm.your.server;
location / {
proxy_pass http://127.0.0.1:4873/;
proxy_set_header Host $host;
}
}

重启 nginx

1
sudo nginx -s reload

verdaccio 配置 (/home/admin/.config/verdaccio/config.yaml)

  • verdaccio 默认使用的是 npm官方的源,可改成淘宝的源
  • 私有npm 里面不包含的包, 例如要安装一个vue 包, 找不到的话, 会被代理到默认 npm 源仓库去下载, 并且会缓存在 ./storage 文件夹
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
# https://github.com/verdaccio/verdaccio/tree/master/conf

# path to a directory with all packages
# 所有包缓存目录
storage: ./storage
# path to a directory with plugins to include
# 插件目录
plugins: ./plugins

# 开启 web 服务,能够通过 web 访问
web:
title: Verdaccio
# comment out to disable gravatar support
# gravatar: false
# by default packages are ordercer ascendant (asc|desc)
# sort_packages: asc

# 验证信息
auth:
htpasswd:
file: ./htpasswd
# Maximum amount of users allowed to register, defaults to "+inf".
# You can set this to -1 to disable registration.
# max_users: 1000

security:
api:
jwt:
sign:
expiresIn: 60d
notBefore: 1
web:
sign:
expiresIn: 7d
notBefore: 1

# a list of other known repositories we can talk to
# 公有仓库配置
uplinks:
npmjs:
# url: https://registry.npmjs.org/
url: https://registry.taobao.org/

packages:
'@*/*':
# scoped packages
# 代理 表示没有的仓库会去 npmjs 里面去找
access: $all
publish: $authenticated
unpublish: $authenticated
proxy: npmjs

'**':
# allow all users (including non-authenticated users) to read and
# publish all packages
#
# you can specify usernames/groupnames (depending on your auth plugin)
# and three keywords: "$all", "$anonymous", "$authenticated"
access: $all

# allow all known users to publish/publish packages
# (anyone can register by default, remember?)
publish: $authenticated
unpublish: $authenticated

# if package is not available locally, proxy requests to 'npmjs' registry
proxy: npmjs

# You can specify HTTP/1.1 server keep alive timeout in seconds for incomming connections.
# A value of 0 makes the http server behave similarly to Node.js versions prior to 8.0.0, which did not have a keep-alive timeout.
# WORKAROUND: Through given configuration you can workaround following issue https://github.com/verdaccio/verdaccio/issues/301. Set to 0 in case 60 is not enought.
server:
keepAliveTimeout: 60

middlewares:
audit:
enabled: true

# log settings
logs:
- { type: stdout, format: pretty, level: http }

使用

  • 切换源
1
2
3
4
# 使用 nrm 切换源
npm i -g nrm
nrm add jhgrrewq http://registry.npm.jhgrrewq.com/
nrm use jhgrrewq
  • 注册用户(或登录)
1
2
npm adduser / npm login
# 输入用户名密码
  • 发布
1
2
# npm publish 标准编写包并发布
npm publish

未命名

发表于 2019-12-11

起步项目

  • 安装官方脚手架 npm install -g create-react-app
  • 创建项目 create-react-app react-study
  • 启动项目 npm start

文件结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
├── README.md              文档
├── public 静态资源
│ ├── favicon.ico
│ ├── index.html
│ └── manifest.json
└── src 源码
├── App.css
├── App.js 根组件
├── App.test.js
├── index.css 全局样式
├── index.js 入口文件
├── logo.svg
└── serviceWorker.js pwa支持
├── package.json npm 依赖

CRA (create-react-app)项目真容

使用 npm run eject 弹出项目真容,会多出两个目录

该操作不可逆

1
2
3
4
5
6
7
8
9
├── config
├── env.js 处理.env环境变量配置文件
├── paths.js 提供各种路径
├── webpack.config.js webpack配置文件
└── webpackDevServer.config.js 测试服务器配置文件
└── scripts 启动、打包和测试脚本
├── build.js 打包脚本
├── start.js 启动脚本
└── test.js 测试脚本

env.js

用来处理 env 文件中配置的变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// node 运行环境:development production test 等
const NODE_ENV = process.env.NODE_ENV

// 待扫描的文件名数组
const dotenvFiles = [
`${paths.dotenv}.${NODE_ENV}.local`, // .env.development.local
`${paths.dotenv}.${NODE_ENV}`, // .env.development
NODE_ENV !== 'test' && `${paths.dotenv}.local`, // .env.local
paths.dotenv, // .env
].filter(Boolean); // 判断文件存在

// 从 .env* 文件中加载文件变量
dotenvFiles.forEach(dotenvFile => {
if (fs.existsSync(dotenvFile)) {
require('dotenv-expand')(
require('dotenv').config({
path: dotenvFile,
})
);
}
});

webpack.config.js

  • 支持 ts
1
2
3
4
5
6
7
8
9
10
11
// Check if TypeScript is setup 启用 ts
const useTypeScript = fs.existsSync(paths.appTsConfig); // 配置 tsconfig.json

// TypeScript type checking ts 相关插件启用
plugins: [
// ...
useTypeScript &&
new ForkTsCheckerWebpackPlugin({
// ...
})
]
  • 支持 sass 和 css 模块化
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// style files regexes
const cssRegex = /\.css$/;
const cssModuleRegex = /\.module\.css$/; // css 模块化
const sassRegex = /\.(scss|sass)$/;
const sassModuleRegex = /\.module\.(scss|sass)$/

module: {
rules: {
// ...
{
// "oneOf" will traverse all following loaders until one will
// match the requirements. When no loader matches it will fall
// back to the "file" loader at the end of the loader list.
// 这里由上至下进行匹配,只有一个匹配后面就不匹配
oneOf: [
{
test: cssRegex,
exclude: cssModuleRegex, // 优先处理 css ,排除模块化 css 文件
use: getStyleLoaders({
importLoaders: 1,
sourceMap: isEnvProduction && shouldUseSourceMap,
}),
sideEffects: true
},
{
test: cssModuleRegex,
use: getStyleLoaders({
importLoaders: 1,
sourceMap: isEnvProduction && shouldUseSourceMap,
modules: {
getLocalIdent: getCSSModuleLocalIdent,
},
})
}
]
}
}
  • 入口
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
entry: [
// Include an alternative client for WebpackDevServer. A client's job is to
// connect to WebpackDevServer by a socket and get notified about changes.
// When you save a file, the client will either apply hot updates (in case
// of CSS changes), or refresh the page (in case of JS changes). When you
// make a syntax error, this client will display a syntax error overlay.
// Note: instead of the default WebpackDevServer client, we use a custom one
// to bring better experience for Create React App users. You can replace
// the line below with these two lines if you prefer the stock client:
// require.resolve('webpack-dev-server/client') + '?/',
// require.resolve('webpack/hot/dev-server'),
isEnvDevelopment &&
require.resolve('react-dev-utils/webpackHotDevClient'), // 开发环境 热更新文件
paths.appIndexJs // src/index 入口文件
].filter(Boolean),

React 和 ReactDom

1
2
3
4
5
6
7
import React from 'react'
import ReactDom from 'react-dom'

// babel-loader 可转换 jsx -> vdom, 需要使用 React.createElement() 等
// <h1>test</h1> -> React.createElement('h1', 'test')
const jsx = <h1>test</h1>
ReactDOM.render(jsx, document.querySelector('#root'))
  • React 负责逻辑控制, 数据 -> vdom
  • ReactDom 渲染实际 dom,vdom -> dom,如果是移动端,要用别的库渲染
  • React 可用 jsx 来描述 UI

JSX

jsx 是一种 js 语法扩展,能很好描述 UI

  • 使用 表达式 {}
  • 表达式可以是 变量 、常量 、函数 、jsx
  • 条件可以通过三元表达式等实现
  • 数组元素可通过 map 等函数实现
  • class for 等属于保留字,class 用 className 替代, for 用 htmlFor 替代
  • style 或 class 可以使用 css 模块化
1
2
import style from './index.module.css'
<h1 className={ style.img }>test</h1>

补充知识

dotenv 从文件加载环境变量

require('dotenv').config() 会默认读取项目根目录下的 .env 文件

1
2
3
4
5
# 可传入路径和编码方式
config({
path: '',
Encoding: ''
})

未命名

发表于 2019-11-28

环境配置难度

虚拟机是带环境安装的一种解决方案,可以在一种操作系统里运行另一种才做系统,但仍有一些缺点:

  • 资源占用多
  • 冗余步骤多
  • 启动慢

linux 容器

由于虚拟机的这些缺点,linux 发展了另一种虚拟化技术:linux 容器

linux 容器不是模拟一个完整的操作系统,而是对进程进行隔离。或者说对容器内进程来说,他接触的资源都是虚拟的,从而实现和底层系统的隔离

  • 启动快

容器内的应用直接就是底层系统的一个进程,而不是虚拟机内部的进程。启动容器相当于启动本机的一个进程,而不是启动一个操作系统

  • 资源占用少

容器只占用需要的资源,多个容器可共享资源;虚拟机因为是完整的操作系统,不可避免需要占用所有资源并独享资源

  • 体积小

容器只要包含用的组件即可;虚拟接则是整个操作系统的打包

  • 一致运行环境
  • 持续交付和部署
  • 更轻松的迁移

docker

docker 属于 linux 容器的一种封装,提供简单易用的容器接口,是目前最流行的 linux 容器解决方案

docker 将应用程序和该程序的依赖打包在一个文件。运行该文件会生成一个虚拟容器。程序在该虚拟容器里运行,无需担心环境问题

docker 接口相当简单,可以方便创建和使用容器。容器还可以进行版本管理、复制、分享、修改等

docker 使用流程

  • 通常会写一个或多个 Dockerfile 文件,一个文件对应一个容器。根据 Dockerfile 的文件配置可以烧录一个镜像(可理解为类似制作一个 USB启动盘)。当制作完成镜像,可以运行这个镜像,运行成功就变成了容器
  • 对于启动和关联多个容器,可编写 docker-compose.yml
  • 对容器进行编排做更多配置,可使用 Kubernetes 在不间断服务情况下更新容器

docker 安装

docker 有两个版本: 社区版(Community Editon, 缩写CE)和企业版(Enterprise Edition,缩写EE), 一般使用社区版

安装

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 升级 apt
sudo apt-get update
# 添加相关软件包
sudo apt-get install apt-transport-https ca-certificates curl software-properties-common
# 下载软件包的合法性,需要添加软件源的 GPG 密钥
curl -fsSL https://mirrors.ustc.edu.cn/docker-ce/linux/ubuntu/gpg | sudo apt-key add -
# source.list 中添加 Docker 软件源
sudo add-apt-repository "deb [arch=amd64] https://mirrors.ustc.edu.cn/docker-ce/linux/ubuntu $(lsb_release -cs) stable"
# 安装 docker CE
sudo apt-get update
sudo apt-get install docker-ce
# 启动 docker CE
sudo groupadd docker
sudo usermod -aG docker $USER
newgrp docker // 更新用户组
# 测试
docker run hello-world
# 使用 -ti 标志可以接管 docker 启动的 shell
docker run -ti ubuntu bash

镜像加速

1
2
3
4
5
6
7
8
9
 # /etc/docker/daemon.json
{
"registry-mirrors": [
"https://dockerhub.azk8s.cn",
"https://reg-mirror.qiniu.com"
] }

sudo systemctl daemon-reload
sudo systemctl restart docker

手动构建镜像

  • docker image 查看本地已有镜像
1
2
3
4
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
ubuntu latest 775349758637 6 weeks ago 64.2MB
hello-world latest fce289e99eb9 11 months ago 1.84kB
  • docker pull 下载镜像 (所有镜像可在 hub.docker.com 中找到)
1
2
3
4
5
6
7
$ docker pull centos
Using default tag: latest
latest: Pulling from library/centos
729ec3a6ada3: Pull complete
Digest: sha256:f94c1d992c193b3dc09e297ffd54d8a4f1dc946c37cbeceb26d35ce1647f88d9
Status: Downloaded newer image for centos:latest
docker.io/library/centos:latest
  • docker rmi 删除镜像 (后面可跟名称或者 imageId)
1
2
3
4
5
$ docker rmi centos
Untagged: centos:latest
Untagged: centos@sha256:f94c1d992c193b3dc09e297ffd54d8a4f1dc946c37cbeceb26d35ce1647f88d9
Deleted: sha256:0f3e07c0138fbe05abcb7a9cc7d63d9bd4c980c3f61fea5efa32e7c4217ef4da
Deleted: sha256:9e607bb861a7d58bece26dd2c02874beedd6a097c1b6eca5255d5eb0d2236983
  • docker run 运行一个镜像,产生一个容器

  • docker ps -a 查看所有容器,不加 a 参数会隐藏停止的容器

1
2
3
4
5
6
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
admin@VM-0-9-ubuntu:~$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
0ba76322c986 ubuntu "/bin/bash" About a minute ago Exited (0) About a minute ago laughing_shirley
287fe66ab63c hello-world "/hello" 19 minutes ago Exited (0) 19 minutes ago affectionate_wu
  • docker rm 删除一个停止容器
  • docker stop 停止运行一个容器
1
2
3
# 移除一个运行的容器会发生错误 
docker rmi fce289e99eb9
Error response from daemon: conflict: unable to delete fce289e99eb9 (must be forced) - image is being used by stopped container 76650e1cb919

编写 Dockerfile 文件

Dockerfile 所有指令可以在 Docker Documentation 中找到

通过 Dockerfile 构建镜像

  • docker build 在当前目录下找到 Dockerfile 文件构建

FROM 指令

自定义镜像基本是基于官方的基础镜像进行更改,使用 From 指令表示以何种镜像为基础

1
FROM Ubuntu

RUN 命令

run 命令用来执行一些 shell 命令,如安装依赖等,通常建议把命令尽量写到一行,因为一个指令就是一个镜像层,安装的命令何在一个层是一种最佳实践

1
RUN apt-get install -y vim

VOLUME 指令

将当前目录下所有文件同步到镜像中,默认 docker 容器中数据都是只读,如果要让他可写,并且可以共享给其他容器必须创建 volume

1
2
3
4
VOLUME ['/code']
WORKDIR /code
RUN node index.js
docker run -v $PWD/app:/code

COPY 和 ADD

这两个命令是把文件复制到容器中,相较而言,ADD 读取远程网络文件更加优秀,一般使用 COPY 即可

1
COPY . /code

EXPORT

将本机端口映射到容器内部端口

1
2
# 将本机 80 端口映射到容器内部 8080 端口
EXPORT 80 8080

WORKDIR

用于切换目录,类似 cd 命令

1
WORKDIR /code

运行一个 server 实例

  • 创建 Dockerfile (命令可使用小写)
1
2
3
4
5
6
7
8
9
10
11
12
# 从 node 镜像的 slim 版本构建, slim 版本是 node 应用最精简容器
from node:slim
# 将当前目录复制到容器 /code 目录下
copy . /code
# 进入 /code 目录
workdir /code
# 暴露容器 80 端口
expose 80
# 安装依赖
run npm install
# 运行 index.js 文件
cmd node index.js
  • 创建 index.js
1
2
3
4
5
const server = require('server')
const { get } = server.router
server({ port: 8080 }, [
get('/', ctx => 'hello docker')
])
  • 安装依赖
1
npm i server -D
  • 忽略 node_modules
1
2
# 创建 .dockerignore
node_modules
  • 构建镜像, t 参数可指定名字
1
docker build . -t my_server
  • 运行镜像,生成容器
1
docker run -p 8080:8080 my_server

docker compose

安装

1
sudo apt-get install docker-compose

命令

  • build
1
2
3
# dicker-compose.yml 藐视了多个容器的属性,运行 build 命令会将自定义 Dockerfile build 成镜像再运行
# 若需要的镜像不存在,会通过 pull 从镜像仓库拉去
docker-compose build
  • up
1
2
3
# up 可启动 docker-compose.yml 中所有服务
# -d 可让服务在后台运行
docker-compose up -d
  • logs
1
2
# logs 查看后台启动的服务日志
docker-compose logs
  • down
1
2
# 将所有通过 up 命令创建的容器停止并销毁
docker-compose down
  • ps
1
2
# 查看通过 docker-compose 启动的容器的详细信息
docker-compose ps
  • stop
1
2
# 停止通过 docker-compose up 启动容器
docker-compose stop
  • start
1
2
# 启动被 stop 命令停止的容器
docker-compose start
  • restart
1
2
# 重启容器
docker-compose restart
  • exec
1
2
# 在某个容器中执行命令, 默认会接管一个 shell
docker-compose exec web sh

未命名

发表于 2019-11-26
1
2
3
4
5
6
7
try{
// 可能抛出异常
} catch(e) {
// 捕获异常
} finally {
// 不管有无异常都执行
}

finally 中的代码不管有无异常都会执行:

  • 如果没有异常发生,try 内代码执行结束后执行
  • 如果异常发生并且被 catch 捕获,在 catch 代码执行结束后执行
  • 如果有异常发生但没被捕获,在异常被抛给上层之前执行
1
2
3
4
5
6
7
8
9
function ret() {
try {
var ret = 0
return ret
} finally {
ret = 1
}
}
ret() // 0
  • 如果 try 或 catch 语句内有 return 语句,则 return 语句在 finally 语句后才执行,但是 finally 不能改变返回值
1
2
3
4
5
6
7
8
9
function ret() {
try {
var ret = 0
return ret
} finally {
return 1
}
}
ret() // 1
  • finally 中有 return 语句,实际会返回 finally 中的返回值,try 和 catch 中的返回值会被覆盖
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function ret() {
try {
return 5/0
} finally {
return 1
}
}
ret() // 1

function ret() {
try {
return 5/0
} finally {
throw new Error('test')
}
}
ret() // Uncaught Error: test
  • finally 不仅 return 语句会覆盖异常,如果 finally 抛出异常,则原异常也会覆盖

综上,应该避免在 finally 中使用 return 语句和排除异常, 如果调用其他代码可能抛出异常,应先捕获异常并进行处理

vue3 composition api 简易实现

发表于 2019-11-19

vue3 已经推出Pre-Alpha 版本, 除了 SSR、keep-alive、transition 和v-on/v-model/v-text/v-pre/v-once/v-html/v-show 等指令外,已完成大部分功能。现在我们对 vue composition-api 尝鲜,并对其响应式做基本实现

阅读全文 »
12…12
jhgrrewq

jhgrrewq

爱好电影,更爱女神阿佳妮;爱网球,更爱罗杰费德勒。网易云音乐深度用户

118 日志
80 标签
GitHub
© 2022 jhgrrewq
由 Hexo 强力驱动
|
主题 — NexT.Mist v5.1.4
备案号 浙ICP备18011306号-1