# 九.geom(Point)
# 1.Point
绘制带有箭头的直线
查看代码详情
<template>
<div ref="map" class="map"></div>
</template>
<script>
export default {
mounted() {
let {
interaction: { Draw },
geom: { Point },
Map,
View,
layer: { Tile: TileLayer, Vector: VectorLayer },
source: { OSM, Vector: VectorSource },
style: { Icon, Stroke, Style },
proj: { get },
} = ol
const raster = new TileLayer({
source: new OSM(),
})
const source = new VectorSource()
let that = this
const styleFunction = function (feature) {
const geometry = feature.getGeometry()
const styles = [
new Style({
stroke: new Stroke({
color: "#ffcc33",
width: 2,
}),
}),
]
geometry.forEachSegment(function (start, end) {
const dx = end[0] - start[0]
const dy = end[1] - start[1]
const rotation = Math.atan2(dy, dx)
styles.push(
new Style({
geometry: new Point(end),
image: new Icon({
src: that.$withBase("/data/arrow.png"),
anchor: [0.75, 0.5],
rotateWithView: true,
rotation: -rotation,
}),
})
)
})
return styles
}
const vector = new VectorLayer({
source: source,
style: styleFunction,
})
const extent = get("EPSG:3857").getExtent().slice()
extent[0] += extent[0]
extent[2] += extent[2]
const map = new Map({
layers: [raster, vector],
target: this.$refs.map,
view: new View({
center: [-11000000, 4600000],
zoom: 4,
extent,
}),
})
map.addInteraction(
new Draw({
source: source,
type: "LineString",
})
)
},
}
</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
66
67
68
69
70
71
72
73
74
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
# 2.风箭
查看代码详情
<template>
<div ref="map" class="map"></div>
</template>
<script>
export default {
mounted() {
let {
Feature,
geom: { Point },
Map,
View,
layer: { Tile: TileLayer, Vector: VectorLayer },
source: { OSM, Vector: VectorSource },
style: { Fill, RegularShape, Stroke, Style },
proj: { fromLonLat },
} = ol
const shaft = new RegularShape({
points: 2,
radius: 5,
stroke: new Stroke({
width: 2,
color: "black",
}),
rotateWithView: true,
})
const head = new RegularShape({
points: 3,
radius: 5,
fill: new Fill({
color: "black",
}),
rotateWithView: true,
})
const styles = [new Style({ image: shaft }), new Style({ image: head })]
const source = new VectorSource({
attributions:
'Weather data by <a href="https://openweathermap.org/current">OpenWeather</a>',
})
const map = new Map({
layers: [
new TileLayer({
source: new OSM(),
}),
new VectorLayer({
source: source,
style: function (feature) {
const wind = feature.get("wind")
// rotate arrow away from wind origin
const angle = ((wind.deg - 180) * Math.PI) / 180
const scale = wind.speed / 10
shaft.setScale([1, scale])
shaft.setRotation(angle)
head.setDisplacement([
0,
head.getRadius() / 2 + shaft.getRadius() * scale,
])
head.setRotation(angle)
return styles
},
}),
],
target: this.$refs.map,
view: new View({
center: [12579156, 3274244],
zoom: 2,
}),
})
fetch(this.$withBase("/data/openweather/weather.json"))
.then(function (response) {
return response.json()
})
.then(function (data) {
const features = []
data.list.forEach(function (report) {
const feature = new Feature(
new Point(fromLonLat([report.coord.lon, report.coord.lat]))
)
feature.setProperties(report)
features.push(feature)
})
source.addFeatures(features)
map.getView().fit(source.getExtent())
})
},
}
</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
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
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
# 3.常规形状
查看代码详情
<template>
<div ref="map" class="map"></div>
</template>
<script>
export default {
mounted() {
let {
Feature,
geom: { Point },
Map,
View,
layer: { Vector: VectorLayer },
source: { Vector: VectorSource },
style: { Fill, RegularShape, Stroke, Style },
} = ol
const stroke = new Stroke({ color: "black", width: 2 })
const fill = new Fill({ color: "red" })
const styles = {
square: new Style({
image: new RegularShape({
fill: fill,
stroke: stroke,
points: 4,
radius: 10,
angle: Math.PI / 4,
}),
}),
rectangle: new Style({
image: new RegularShape({
fill: fill,
stroke: stroke,
radius: 10 / Math.SQRT2,
radius2: 10,
points: 4,
angle: 0,
scale: [1, 0.5],
}),
}),
triangle: new Style({
image: new RegularShape({
fill: fill,
stroke: stroke,
points: 3,
radius: 10,
rotation: Math.PI / 4,
angle: 0,
}),
}),
star: new Style({
image: new RegularShape({
fill: fill,
stroke: stroke,
points: 5,
radius: 10,
radius2: 4,
angle: 0,
}),
}),
cross: new Style({
image: new RegularShape({
fill: fill,
stroke: stroke,
points: 4,
radius: 10,
radius2: 0,
angle: 0,
}),
}),
x: new Style({
image: new RegularShape({
fill: fill,
stroke: stroke,
points: 4,
radius: 10,
radius2: 0,
angle: Math.PI / 4,
}),
}),
stacked: [
new Style({
image: new RegularShape({
fill: fill,
stroke: stroke,
points: 4,
radius: 5,
angle: Math.PI / 4,
displacement: [0, 10],
}),
}),
new Style({
image: new RegularShape({
fill: fill,
stroke: stroke,
points: 4,
radius: 10,
angle: Math.PI / 4,
}),
}),
],
}
const styleKeys = [
"x",
"cross",
"star",
"triangle",
"square",
"rectangle",
"stacked",
]
const count = 250
const features = new Array(count)
const e = 4500000
for (let i = 0; i < count; ++i) {
const coordinates = [2 * e * Math.random() - e, 2 * e * Math.random() - e]
features[i] = new Feature(new Point(coordinates))
features[i].setStyle(
styles[styleKeys[Math.floor(Math.random() * styleKeys.length)]]
)
}
const source = new VectorSource({
features: features,
})
const vectorLayer = new VectorLayer({
source: source,
})
const map = new Map({
layers: [vectorLayer],
target: this.$refs.map,
view: new View({
center: [12579156, 3274244],
zoom: 2,
}),
})
},
}
</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
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
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
# 4.层 Z 索引
查看代码详情
<template>
<div>
<div ref="map" class="map"></div>
<div>
<label for="idx1">
<input type="number" id="idx1" />
Square layer Z-index </label
><br />
<label for="idx2">
<input type="number" id="idx2" />
Triangle layer Z-index
</label>
</div>
</div>
</template>
<script>
export default {
mounted() {
let {
Feature,
geom: { Point },
Map,
View,
layer: { Vector: VectorLayer },
source: { Vector: VectorSource },
style: { Fill, RegularShape, Stroke, Style },
} = ol;
const stroke = new Stroke({ color: "black", width: 1 });
const styles = {
square: new Style({
image: new RegularShape({
fill: new Fill({ color: "blue" }),
stroke: stroke,
points: 4,
radius: 80,
angle: Math.PI / 4,
}),
}),
triangle: new Style({
image: new RegularShape({
fill: new Fill({ color: "red" }),
stroke: stroke,
points: 3,
radius: 80,
rotation: Math.PI / 4,
angle: 0,
}),
}),
star: new Style({
image: new RegularShape({
fill: new Fill({ color: "green" }),
stroke: stroke,
points: 5,
radius: 80,
radius2: 4,
angle: 0,
}),
}),
};
function createLayer(coordinates, style, zIndex) {
const feature = new Feature(new Point(coordinates));
feature.setStyle(style);
const source = new VectorSource({
features: [feature],
});
const vectorLayer = new VectorLayer({
source: source,
});
vectorLayer.setZIndex(zIndex);
return vectorLayer;
}
const layer0 = createLayer([40, 40], styles["star"]);
const layer1 = createLayer([0, 0], styles["square"], 1);
const layer2 = createLayer([0, 40], styles["triangle"], 0);
const layers = [];
layers.push(layer1);
layers.push(layer2);
const map = new Map({
layers: layers,
target: this.$refs.map,
view: new View({
center: [12579156, 3274244],
zoom: 18,
}),
});
layer0.setMap(map);
function bindInputs(id, layer) {
const idxInput = document.getElementById("idx" + id);
idxInput.onchange = function () {
layer.setZIndex(parseInt(this.value, 10) || 0);
};
idxInput.value = String(layer.getZIndex());
}
bindInputs(1, layer1);
bindInputs(2, layer2);
},
};
</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
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
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
# 5.图标比例尺
查看代码详情
<template>
<div ref="map" class="map"><div id="popup"></div></div>
</template>
<script>
export default {
mounted() {
let {
Feature,
Overlay,
geom: { Point },
Map,
View,
layer: { Tile: TileLayer, Vector: VectorLayer },
source: { TileJSON, Vector: VectorSource },
style: { Icon, Style, Text },
proj: { fromLonLat },
render: { getVectorContext },
} = ol
const rasterLayer = new TileLayer({
source: new TileJSON({
url: "https://a.tiles.mapbox.com/v3/aj.1x1-degrees.json?secure=1",
crossOrigin: "",
}),
})
const iconFeature = new Feature({
geometry: new Point(fromLonLat([0, -10])),
name: "Fish.1",
})
const feature1 = new Feature({
geometry: new Point(fromLonLat([0, -10])),
name: "Fish.1 Island",
})
const feature2 = new Feature({
geometry: new Point(fromLonLat([-30, 10])),
name: "Fish.2 Island",
})
const iconStyle = new Style({
image: new Icon({
anchor: [0.5, 0.9],
src: this.$withBase("/data/fish.png"),
crossOrigin: "",
scale: [0, 0],
rotation: Math.PI / 4,
}),
text: new Text({
text: "FISH\nTEXT",
scale: [0, 0],
rotation: Math.PI / 4,
textAlign: "center",
textBaseline: "top",
}),
})
let i = 0
let j = 45
iconFeature.setStyle(function () {
const x = Math.sin((i * Math.PI) / 180) * 3
const y = Math.sin((j * Math.PI) / 180) * 4
iconStyle.getImage().setScale([x, y])
iconStyle.getText().setScale([x, y])
return iconStyle
})
rasterLayer.on("postrender", function (event) {
const vectorContext = getVectorContext(event)
const x = Math.cos((i * Math.PI) / 180) * 3
const y = Math.cos((j * Math.PI) / 180) * 4
iconStyle.getImage().setScale([x, y])
iconStyle.getText().setScale([x, y])
vectorContext.drawFeature(feature2, iconStyle)
})
const vectorSource = new VectorSource({
features: [iconFeature, feature1, feature2],
})
const vectorLayer = new VectorLayer({
source: vectorSource,
})
const map = new Map({
layers: [rasterLayer, vectorLayer],
target: this.$refs.map,
view: new View({
center: fromLonLat([-15, 0]),
zoom: 3,
}),
})
setInterval(function () {
i = (i + 4) % 360
j = (j + 5) % 360
vectorSource.changed()
}, 1000)
const element = document.getElementById("popup")
const popup = new Overlay({
element: element,
positioning: "bottom-center",
stopEvent: false,
})
map.addOverlay(popup)
map.on("click", function (evt) {
const feature = map.forEachFeatureAtPixel(evt.pixel, function (feature) {
return feature
})
$(element).popover("dispose")
if (feature) {
popup.setPosition(evt.coordinate)
$(element).popover({
placement: "top",
html: true,
animation: false,
content: feature.get("name"),
})
$(element).popover("show")
}
})
map.on("pointermove", function (e) {
const pixel = map.getEventPixel(e.originalEvent)
const hit = map.hasFeatureAtPixel(pixel)
map.getTarget().style.cursor = hit ? "pointer" : ""
})
map.on("movestart", function () {
$(element).popover("dispose")
})
},
}
</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
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
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
# 6.图标精灵与 WebGL
查看代码详情
<template>
<div>
<div ref="map" class="map"></div>
<div>Current sighting: <span id="info"></span></div>
<div>
<label for="shape-filter">Filter by UFO shape:</label>
<select id="shape-filter"></select>
</div>
</div>
</template>
<script>
export default {
mounted() {
let {
Feature,
geom: { Point },
Map,
View,
layer: { Tile: TileLayer, WebGLPoints: WebGLPointsLayer },
source: { XYZ, Vector },
proj: { fromLonLat },
} = ol;
const attributions =
'<a href="https://www.maptiler.com/copyright/" target="_blank">© MapTiler</a> ' +
'<a href="https://www.openstreetmap.org/copyright" target="_blank">© OpenStreetMap contributors</a>';
const map = new Map({
layers: [
new TileLayer({
source: new XYZ({
attributions: attributions,
url:
"https://api.maptiler.com/tiles/satellite/{z}/{x}/{y}.jpg?key=" +
mapkeys.maptiler,
tileSize: 512,
}),
}),
],
target: this.$refs.map,
view: new View({
center: [0, 4000000],
zoom: 2,
}),
});
const vectorSource = new Vector({
features: [],
attributions: "National UFO Reporting Center",
});
const oldColor = [255, 160, 110];
const newColor = [180, 255, 200];
const size = 16;
const style = {
variables: {
filterShape: "all",
},
filter: [
"case",
["!=", ["var", "filterShape"], "all"],
["==", ["get", "shape"], ["var", "filterShape"]],
true,
],
symbol: {
symbolType: "image",
src: this.$withBase("/data/ufo_shapes.png"),
size: size,
color: [
"interpolate",
["linear"],
["get", "year"],
1950,
oldColor,
2013,
newColor,
],
rotateWithView: false,
offset: [0, 0],
textureCoord: [
"match",
["get", "shape"],
"light",
[0, 0, 0.25, 0.5],
"sphere",
[0.25, 0, 0.5, 0.5],
"circle",
[0.25, 0, 0.5, 0.5],
"disc",
[0.5, 0, 0.75, 0.5],
"oval",
[0.5, 0, 0.75, 0.5],
"triangle",
[0.75, 0, 1, 0.5],
"fireball",
[0, 0.5, 0.25, 1],
[0.75, 0.5, 1, 1],
],
},
};
const shapeTypes = {
all: 0,
};
const shapeSelect = document.getElementById("shape-filter");
shapeSelect.addEventListener("input", function () {
style.variables.filterShape =
shapeSelect.options[shapeSelect.selectedIndex].value;
map.render();
});
function fillShapeSelect() {
Object.keys(shapeTypes)
.sort(function (a, b) {
return shapeTypes[b] - shapeTypes[a];
})
.forEach(function (shape) {
const option = document.createElement("option");
option.text = `${shape} (${shapeTypes[shape]} sightings)`;
option.value = shape;
shapeSelect.appendChild(option);
});
}
const client = new XMLHttpRequest();
client.open("GET", this.$withBase("/data/csv/ufo_sighting_data.csv"));
client.onload = function () {
const csv = client.responseText;
const features = [];
let prevIndex = csv.indexOf("\n") + 1;
let curIndex;
while ((curIndex = csv.indexOf("\n", prevIndex)) != -1) {
const line = csv.substr(prevIndex, curIndex - prevIndex).split(",");
prevIndex = curIndex + 1;
const coords = fromLonLat([parseFloat(line[5]), parseFloat(line[4])]);
if (isNaN(coords[0]) || isNaN(coords[1])) {
continue;
}
const shape = line[2];
shapeTypes[shape] = (shapeTypes[shape] ? shapeTypes[shape] : 0) + 1;
shapeTypes["all"]++;
features.push(
new Feature({
datetime: line[0],
year: parseInt(/[0-9]{4}/.exec(line[0])[0]),
shape: shape,
duration: line[3],
geometry: new Point(coords),
})
);
}
vectorSource.addFeatures(features);
fillShapeSelect();
};
client.send();
map.addLayer(
new WebGLPointsLayer({
source: vectorSource,
style: style,
})
);
const info = document.getElementById("info");
map.on("pointermove", function (evt) {
if (map.getView().getInteracting() || map.getView().getAnimating()) {
return;
}
const pixel = evt.pixel;
info.innerText = "";
map.forEachFeatureAtPixel(pixel, function (feature) {
const datetime = feature.get("datetime");
const duration = feature.get("duration");
const shape = feature.get("shape");
info.innerText =
"On " +
datetime +
", lasted " +
duration +
' seconds and had a "' +
shape +
'" shape.';
});
});
},
};
</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
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
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
# 7.图标符号
查看代码详情
<template>
<div ref="map" class="map"><div id="popup"></div></div>
</template>
<script>
export default {
mounted() {
let {
Feature,
Overlay,
geom: { Point },
Map,
View,
layer: { Tile: TileLayer, Vector: VectorLayer },
source: { TileJSON, Vector: VectorSource },
style: { Icon, Style },
} = ol;
const iconFeature = new Feature({
geometry: new Point([0, 0]),
name: "Null Island",
population: 4000,
rainfall: 500,
});
const iconStyle = new Style({
image: new Icon({
anchor: [0.5, 46],
anchorXUnits: "fraction",
anchorYUnits: "pixels",
src: this.$withBase("/data/icon.png"),
}),
});
iconFeature.setStyle(iconStyle);
const vectorSource = new VectorSource({
features: [iconFeature],
});
const vectorLayer = new VectorLayer({
source: vectorSource,
});
const rasterLayer = new TileLayer({
source: new TileJSON({
url: "https://a.tiles.mapbox.com/v3/aj.1x1-degrees.json?secure=1",
crossOrigin: "",
}),
});
const map = new Map({
layers: [rasterLayer, vectorLayer],
target: this.$refs.map,
view: new View({
center: [12579156, 3274244],
zoom: 3,
}),
});
const element = document.getElementById("popup");
const popup = new Overlay({
element: element,
positioning: "bottom-center",
stopEvent: false,
});
map.addOverlay(popup);
map.on("click", function (evt) {
const feature = map.forEachFeatureAtPixel(evt.pixel, function (feature) {
return feature;
});
if (feature) {
popup.setPosition(evt.coordinate);
$(element).popover({
placement: "top",
html: true,
content: feature.get("name"),
});
$(element).popover("show");
} else {
$(element).popover("dispose");
}
});
map.on("pointermove", function (e) {
const pixel = map.getEventPixel(e.originalEvent);
const hit = map.hasFeatureAtPixel(pixel);
map.getTarget().style.cursor = hit ? "pointer" : "";
});
map.on("movestart", function () {
$(element).popover("dispose");
});
},
};
</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
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
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
# 8.图标颜色
查看代码详情
<template>
<div ref="map" class="map"></div>
</template>
<script>
export default {
mounted() {
let {
Feature,
geom: { Point },
Map,
View,
layer: { Tile: TileLayer, Vector: VectorLayer },
source: { TileJSON, Vector: VectorSource },
style: { Icon, Style },
proj: { fromLonLat },
} = ol
const rome = new Feature({
geometry: new Point(fromLonLat([12.5, 41.9])),
})
const london = new Feature({
geometry: new Point(fromLonLat([-0.12755, 51.507222])),
})
const madrid = new Feature({
geometry: new Point(fromLonLat([-3.683333, 40.4])),
})
const paris = new Feature({
geometry: new Point(fromLonLat([2.353, 48.8566])),
})
const berlin = new Feature({
geometry: new Point(fromLonLat([13.3884, 52.5169])),
})
rome.setStyle(
new Style({
image: new Icon({
color: "#BADA55",
crossOrigin: "anonymous",
imgSize: [20, 20],
src: this.$withBase("/data/square.svg"),
}),
})
)
london.setStyle(
new Style({
image: new Icon({
color: "rgba(255, 0, 0, .5)",
crossOrigin: "anonymous",
src: this.$withBase("/data/bigdot.png"),
scale: 0.2,
}),
})
)
madrid.setStyle(
new Style({
image: new Icon({
crossOrigin: "anonymous",
src: this.$withBase("/data/bigdot.png"),
scale: 0.2,
}),
})
)
paris.setStyle(
new Style({
image: new Icon({
color: "#8959A8",
crossOrigin: "anonymous",
imgSize: [20, 20],
src: this.$withBase("/data/dot.svg"),
}),
})
)
berlin.setStyle(
new Style({
image: new Icon({
crossOrigin: "anonymous",
imgSize: [20, 20],
src: this.$withBase("/data/dot.svg"),
}),
})
)
const vectorSource = new VectorSource({
features: [rome, london, madrid, paris, berlin],
})
const vectorLayer = new VectorLayer({
source: vectorSource,
})
const rasterLayer = new TileLayer({
source: new TileJSON({
url:
"https://a.tiles.mapbox.com/v4/aj.1x1-degrees.json?secure=1&access_token=" +
mapkeys.mapbox,
crossOrigin: "anonymous",
}),
})
const map = new Map({
layers: [rasterLayer, vectorLayer],
target: this.$refs.map,
view: new View({
center: fromLonLat([2.896372, 44.6024]),
zoom: 3,
}),
})
},
}
</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
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
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
# 9.图标修改
查看代码详情
<template>
<div ref="map" class="map"></div>
</template>
<script>
export default {
mounted() {
let {
Feature,
geom: { Point },
Map,
View,
layer: { Tile: TileLayer, Vector: VectorLayer },
source: { TileJSON, Vector: VectorSource },
style: { Icon, Style },
interaction: { Modify },
} = ol;
const iconFeature = new Feature({
geometry: new Point([0, 0]),
name: "Null Island",
population: 4000,
rainfall: 500,
});
const iconStyle = new Style({
image: new Icon({
anchor: [0.5, 46],
anchorXUnits: "fraction",
anchorYUnits: "pixels",
src: this.$withBase("/data/icon.png"),
}),
});
iconFeature.setStyle(iconStyle);
const vectorSource = new VectorSource({
features: [iconFeature],
});
const vectorLayer = new VectorLayer({
source: vectorSource,
});
const rasterLayer = new TileLayer({
source: new TileJSON({
url:
"https://a.tiles.mapbox.com/v4/aj.1x1-degrees.json?secure=1&access_token=" +
mapkeys.mapbox,
crossOrigin: "anonymous",
}),
});
const target = this.$refs.map;
const map = new Map({
layers: [rasterLayer, vectorLayer],
target: target,
view: new View({
center: [12579156, 3274244],
zoom: 3,
}),
});
const modify = new Modify({
hitDetection: vectorLayer,
source: vectorSource,
});
modify.on(["modifystart", "modifyend"], function (evt) {
target.style.cursor = evt.type === "modifystart" ? "grabbing" : "pointer";
});
const overlaySource = modify.getOverlay().getSource();
overlaySource.on(["addfeature", "removefeature"], function (evt) {
target.style.cursor = evt.type === "addfeature" ? "pointer" : "";
});
map.addInteraction(modify);
},
};
</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
66
67
68
69
70
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
# 10.图标像素操作
查看代码详情
<template>
<div id="map" class="map">
<div id="popup"></div>
</div>
</template>
<script>
export default {
mounted() {
let {
Feature,
Overlay,
geom: { Point },
Map,
View,
layer: { Tile: TileLayer, Vector: VectorLayer },
source: { TileJSON, Vector: VectorSource },
style: { Icon, Style, Text },
proj: { fromLonLat },
render: { getVectorContext },
} = ol
const rasterLayer = new TileLayer({
source: new TileJSON({
url:
"https://a.tiles.mapbox.com/v4/aj.1x1-degrees.json?secure=1&access_token=" +
mapkeys.mapbox,
crossOrigin: "anonymous",
}),
})
const iconFeature = new Feature({
geometry: new Point(fromLonLat([0, -10])),
name: "Fish.1",
})
const feature1 = new Feature({
geometry: new Point(fromLonLat([0, -10])),
name: "Fish.1 Island",
})
const feature2 = new Feature({
geometry: new Point(fromLonLat([-30, 10])),
name: "Fish.2 Island",
})
const iconStyle = new Style({
image: new Icon({
anchor: [0.5, 0.9],
src: this.$withBase("/data/fish.png"),
crossOrigin: "",
scale: [0, 0],
rotation: Math.PI / 4,
}),
text: new Text({
text: "FISH\nTEXT",
scale: [0, 0],
rotation: Math.PI / 4,
textAlign: "center",
textBaseline: "top",
}),
})
let i = 0
let j = 45
iconFeature.setStyle(function () {
const x = Math.sin((i * Math.PI) / 180) * 3
const y = Math.sin((j * Math.PI) / 180) * 4
iconStyle.getImage().setScale([x, y])
iconStyle.getText().setScale([x, y])
return iconStyle
})
rasterLayer.on("postrender", function (event) {
const vectorContext = getVectorContext(event)
const x = Math.cos((i * Math.PI) / 180) * 3
const y = Math.cos((j * Math.PI) / 180) * 4
iconStyle.getImage().setScale([x, y])
iconStyle.getText().setScale([x, y])
vectorContext.drawFeature(feature2, iconStyle)
})
const vectorSource = new VectorSource({
features: [iconFeature, feature1, feature2],
})
const vectorLayer = new VectorLayer({
source: vectorSource,
})
const map = new Map({
layers: [rasterLayer, vectorLayer],
target: this.$refs.map,
view: new View({
center: fromLonLat([-15, 0]),
zoom: 3,
}),
})
setInterval(function () {
i = (i + 4) % 360
j = (j + 5) % 360
vectorSource.changed()
}, 1000)
const element = document.getElementById("popup")
const popup = new Overlay({
element: element,
positioning: "bottom-center",
stopEvent: false,
})
map.addOverlay(popup)
map.on("click", function (evt) {
const feature = map.forEachFeatureAtPixel(evt.pixel, function (feature) {
return feature
})
$(element).popover("dispose")
if (feature) {
popup.setPosition(evt.coordinate)
$(element).popover({
placement: "top",
html: true,
animation: false,
content: feature.get("name"),
})
$(element).popover("show")
}
})
map.on("pointermove", function (e) {
const pixel = map.getEventPixel(e.originalEvent)
const hit = map.hasFeatureAtPixel(pixel)
map.getTarget().style.cursor = hit ? "pointer" : ""
})
map.on("movestart", function () {
$(element).popover("dispose")
})
},
}
</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
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
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
# 11.立即渲染(地理)
查看代码详情
<template>
<div ref="map" class="map"></div>
</template>
<script>
export default {
mounted() {
let {
geom: { Point },
Map,
View,
layer: { Tile: TileLayer },
source: { Stamen },
style: { Circle, Fill, Style },
proj: { useGeographic },
render: { getVectorContext },
easing: { upAndDown },
} = ol
useGeographic()
const layer = new TileLayer({
source: new Stamen({
layer: "toner",
}),
})
const map = new Map({
layers: [layer],
target: this.$refs.map,
view: new View({
center: [12579156, 3274244],
zoom: 2,
}),
})
const image = new Circle({
radius: 8,
fill: new Fill({ color: "rgb(255, 153, 0)" }),
})
const style = new Style({
image: image,
})
const n = 1000
const geometries = new Array(n)
for (let i = 0; i < n; ++i) {
const lon = 360 * Math.random() - 180
const lat = 180 * Math.random() - 90
geometries[i] = new Point([lon, lat])
}
layer.on("postrender", function (event) {
const vectorContext = getVectorContext(event)
for (let i = 0; i < n; ++i) {
const importance = upAndDown(Math.pow((n - i) / n, 0.15))
if (importance < 0.1) {
continue
}
image.setOpacity(importance)
image.setScale(importance)
vectorContext.setStyle(style)
vectorContext.drawGeometry(geometries[i])
}
const lon = 360 * Math.random() - 180
const lat = 180 * Math.random() - 90
geometries.push(new Point([lon, lat]))
geometries.shift()
map.render()
})
},
}
</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
66
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
# 12.动画 GIF
查看代码详情
<template>
<div ref="map" class="map"></div>
</template>
<script>
export default {
mounted() {
let {
Feature,
Map,
geom: { Point },
View,
layer: { Tile: TileLayer, Vector: VectorLayer },
source: { Stamen, Vector: VectorSource },
style: { Icon, Style },
} = ol
const iconFeature = new Feature({
geometry: new Point([0, 0]),
})
const vectorSource = new VectorSource({
features: [iconFeature],
})
const vectorLayer = new VectorLayer({
source: vectorSource,
})
const rasterLayer = new TileLayer({
source: new Stamen({
layer: "toner",
}),
})
const map = new Map({
layers: [rasterLayer, vectorLayer],
target: this.$refs.map,
view: new View({
center: [12579156, 3274244],
zoom: 2,
}),
})
const gifUrl = this.$withBase("/data/globe.gif")
const gif = gifler(gifUrl)
gif.frames(
document.createElement("canvas"),
function (ctx, frame) {
if (!iconFeature.getStyle()) {
iconFeature.setStyle(
new Style({
image: new Icon({
img: ctx.canvas,
imgSize: [frame.width, frame.height],
opacity: 0.8,
}),
})
)
}
ctx.clearRect(0, 0, frame.width, frame.height)
ctx.drawImage(frame.buffer, frame.x, frame.y)
map.render()
},
true
)
map.on("pointermove", function (e) {
const pixel = map.getEventPixel(e.originalEvent)
const hit = map.hasFeatureAtPixel(pixel)
map.getTarget().style.cursor = hit ? "pointer" : ""
})
},
}
</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
66
67
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
# 13.地理位置
查看代码详情
<template>
<div>
<div ref="map" class="map"></div>
<div id="info" style="display: none"></div>
<label for="track">
轨道位置
<input id="track" type="checkbox" />
</label>
<p>
位置精度 : <code id="accuracy"></code> 海拔高度 :
<code id="altitude"></code> 高度精度 :
<code id="altitudeAccuracy"></code> 标题 :
<code id="heading"></code> 速度 : <code id="speed"></code>
</p>
</div>
</template>
<script>
export default {
mounted() {
let {
Feature,
Geolocation,
geom: { Point },
Map,
View,
layer: { Tile: TileLayer, Vector: VectorLayer },
source: { OSM, Vector: VectorSource },
style: { Circle: CircleStyle, Fill, Stroke, Style },
} = ol;
const view = new View({
center: [12579156, 3274244],
zoom: 2,
});
const map = new Map({
layers: [
new TileLayer({
source: new OSM(),
}),
],
target: this.$refs.map,
view: view,
});
const geolocation = new Geolocation({
trackingOptions: {
enableHighAccuracy: true,
},
projection: view.getProjection(),
});
function el(id) {
return document.getElementById(id);
}
el("track").addEventListener("change", function () {
geolocation.setTracking(this.checked);
});
geolocation.on("change", function () {
el("accuracy").innerText = geolocation.getAccuracy() + " [m]";
el("altitude").innerText = geolocation.getAltitude() + " [m]";
el("altitudeAccuracy").innerText =
geolocation.getAltitudeAccuracy() + " [m]";
el("heading").innerText = geolocation.getHeading() + " [rad]";
el("speed").innerText = geolocation.getSpeed() + " [m/s]";
});
geolocation.on("error", function (error) {
const info = document.getElementById("info");
info.innerHTML = error.message;
info.style.display = "";
});
const accuracyFeature = new Feature();
geolocation.on("change:accuracyGeometry", function () {
accuracyFeature.setGeometry(geolocation.getAccuracyGeometry());
});
const positionFeature = new Feature();
positionFeature.setStyle(
new Style({
image: new CircleStyle({
radius: 6,
fill: new Fill({
color: "#3399CC",
}),
stroke: new Stroke({
color: "#fff",
width: 2,
}),
}),
})
);
geolocation.on("change:position", function () {
const coordinates = geolocation.getPosition();
positionFeature.setGeometry(coordinates ? new Point(coordinates) : null);
});
new VectorLayer({
map: map,
source: new VectorSource({
features: [accuracyFeature, positionFeature],
}),
});
},
};
</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
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
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
# 14.使用 WebGL 过滤功能
这个例子展示了如何使用 ol/layer/WebGLPoints 文字样式来动态过滤大量的点几何图形。上面的地图基于 NASA 的数据集,其中包含 45k 记录的陨石着陆点。每颗陨石在地图上都用一个圆圈标记(圆圈越大,物体越重)。增加了一个脉冲效应,它被影响的年份稍微抵消了。
调整滑块会导致日期范围之外的对象从地图中过滤掉。这是通过改变 style 提供给 WebGL 层的对象中的变量来完成的。另请注意,最后一段代码是确保地图在每一帧都刷新自身所必需的。
查看代码详情
<template>
<div>
<div ref="map" class="map"></div>
<form>
<div id="status">
显示 <span class="min-year"></span> 到
<span class="max-year"></span>之间的范围
</div>
<label for="min-year">最低年份:</label>
<input
id="min-year"
type="range"
min="1850"
max="2015"
step="1"
value="1850"
/>
<label for="max-year">最高年份:</label>
<input
id="max-year"
type="range"
min="1850"
max="2015"
step="1"
value="2015"
/>
</form>
</div>
</template>
<script>
export default {
mounted() {
let {
Feature,
geom: { Point },
Map,
View,
layer: { Tile: TileLayer, WebGLPoints: WebGLPointsLayer },
source: { Stamen, Vector: VectorSource },
proj: { fromLonLat },
} = ol;
const vectorSource = new VectorSource({
attributions: "NASA",
});
const oldColor = "rgba(242,56,22,0.61)";
const newColor = "#ffe52c";
const period = 12;
const animRatio = [
"^",
[
"/",
[
"%",
[
"+",
["time"],
["interpolate", ["linear"], ["get", "year"], 1850, 0, 2015, period],
],
period,
],
period,
],
0.5,
];
const style = {
variables: {
minYear: 1850,
maxYear: 2015,
},
filter: [
"between",
["get", "year"],
["var", "minYear"],
["var", "maxYear"],
],
symbol: {
symbolType: "circle",
size: [
"*",
["interpolate", ["linear"], ["get", "mass"], 0, 8, 200000, 26],
["-", 1.75, ["*", animRatio, 0.75]],
],
color: ["interpolate", ["linear"], animRatio, 0, newColor, 1, oldColor],
opacity: ["-", 1.0, ["*", animRatio, 0.75]],
},
};
const minYearInput = document.getElementById("min-year");
const maxYearInput = document.getElementById("max-year");
function updateMinYear() {
style.variables.minYear = parseInt(minYearInput.value);
updateStatusText();
}
function updateMaxYear() {
style.variables.maxYear = parseInt(maxYearInput.value);
updateStatusText();
}
function updateStatusText() {
const div = document.getElementById("status");
div.querySelector("span.min-year").textContent = minYearInput.value;
div.querySelector("span.max-year").textContent = maxYearInput.value;
}
minYearInput.addEventListener("input", updateMinYear);
minYearInput.addEventListener("change", updateMinYear);
maxYearInput.addEventListener("input", updateMaxYear);
maxYearInput.addEventListener("change", updateMaxYear);
updateStatusText();
const client = new XMLHttpRequest();
client.open("GET", this.$withBase("/data/csv/meteorite_landings.csv"));
client.onload = function () {
const csv = client.responseText;
const features = [];
let prevIndex = csv.indexOf("\n") + 1;
let curIndex;
while ((curIndex = csv.indexOf("\n", prevIndex)) != -1) {
const line = csv.substr(prevIndex, curIndex - prevIndex).split(",");
prevIndex = curIndex + 1;
const coords = fromLonLat([parseFloat(line[4]), parseFloat(line[3])]);
if (isNaN(coords[0]) || isNaN(coords[1])) {
continue;
}
features.push(
new Feature({
mass: parseFloat(line[1]) || 0,
year: parseInt(line[2]) || 0,
geometry: new Point(coords),
})
);
}
vectorSource.addFeatures(features);
};
client.send();
const map = new Map({
layers: [
new TileLayer({
source: new Stamen({
layer: "toner",
}),
}),
new WebGLPointsLayer({
style: style,
source: vectorSource,
disableHitDetection: true,
}),
],
target: this.$refs.map,
view: new View({
center: [12579156, 3274244],
zoom: 2,
}),
});
function animate() {
map.render();
window.requestAnimationFrame(animate);
}
animate();
},
};
</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
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
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