# 六.style
# 1.带方向的地理位置跟踪
查看代码详情
<template>
<div>
<div ref="map" class="map"></div>
<div id="info"></div>
<img
id="geolocation_marker"
:src="$withBase('/data/geolocation_marker.png')"
/>
<div class="button">
<button id="geolocate">Geolocate Me!</button>
<button id="simulate">Simulate</button>
</div>
</div>
</template>
<script>
export default {
mounted() {
let {
Overlay,
Geolocation,
geom: { LineString },
Map,
View,
layer: { Tile: TileLayer },
source: { OSM },
proj: { fromLonLat },
} = ol;
const view = new View({
center: fromLonLat([5.8713, 45.6452]),
zoom: 19,
});
const tileLayer = new TileLayer({
source: new OSM(),
});
// creating the map
const map = new Map({
layers: [tileLayer],
target: this.$refs.map,
view: view,
});
// Geolocation marker
const markerEl = document.getElementById("geolocation_marker");
const marker = new Overlay({
positioning: "center-center",
element: markerEl,
stopEvent: false,
});
map.addOverlay(marker);
// LineString to store the different geolocation positions. This LineString
// is time aware.
// The Z dimension is actually used to store the rotation (heading).
const positions = new LineString([], "XYZM");
// Geolocation Control
const geolocation = new Geolocation({
projection: view.getProjection(),
trackingOptions: {
maximumAge: 10000,
enableHighAccuracy: true,
timeout: 600000,
},
});
let deltaMean = 500; // the geolocation sampling period mean in ms
// Listen to position changes
geolocation.on("change", function () {
const position = geolocation.getPosition();
const accuracy = geolocation.getAccuracy();
const heading = geolocation.getHeading() || 0;
const speed = geolocation.getSpeed() || 0;
const m = Date.now();
addPosition(position, heading, m, speed);
const coords = positions.getCoordinates();
const len = coords.length;
if (len >= 2) {
deltaMean = (coords[len - 1][3] - coords[0][3]) / (len - 1);
}
const html = [
"Position: " + position[0].toFixed(2) + ", " + position[1].toFixed(2),
"Accuracy: " + accuracy,
"Heading: " + Math.round(radToDeg(heading)) + "°",
"Speed: " + (speed * 3.6).toFixed(1) + " km/h",
"Delta: " + Math.round(deltaMean) + "ms",
].join("<br />");
document.getElementById("info").innerHTML = html;
});
geolocation.on("error", function () {
alert("geolocation error");
// FIXME we should remove the coordinates in positions
});
// convert radians to degrees
function radToDeg(rad) {
return (rad * 360) / (Math.PI * 2);
}
// convert degrees to radians
function degToRad(deg) {
return (deg * Math.PI * 2) / 360;
}
// modulo for negative values
function mod(n) {
return ((n % (2 * Math.PI)) + 2 * Math.PI) % (2 * Math.PI);
}
function addPosition(position, heading, m, speed) {
const x = position[0];
const y = position[1];
const fCoords = positions.getCoordinates();
const previous = fCoords[fCoords.length - 1];
const prevHeading = previous && previous[2];
if (prevHeading) {
let headingDiff = heading - mod(prevHeading);
if (Math.abs(headingDiff) > Math.PI) {
const sign = headingDiff >= 0 ? 1 : -1;
headingDiff = -sign * (2 * Math.PI - Math.abs(headingDiff));
}
heading = prevHeading + headingDiff;
}
positions.appendCoordinate([x, y, heading, m]);
positions.setCoordinates(positions.getCoordinates().slice(-20));
if (heading && speed) {
markerEl.src = this.$withBase("/data/geolocation_marker_heading.png");
} else {
markerEl.src = this.$withBase("/data/geolocation_marker.png");
}
}
function getCenterWithHeading(position, rotation, resolution) {
const size = map.getSize();
const height = size[1];
return [
position[0] - (Math.sin(rotation) * height * resolution * 1) / 4,
position[1] + (Math.cos(rotation) * height * resolution * 1) / 4,
];
}
let previousM = 0;
function updateView() {
let m = Date.now() - deltaMean * 1.5;
m = Math.max(m, previousM);
previousM = m;
const c = positions.getCoordinateAtM(m, true);
if (c) {
view.setCenter(getCenterWithHeading(c, -c[2], view.getResolution()));
view.setRotation(-c[2]);
marker.setPosition(c);
map.render();
}
}
const geolocateBtn = document.getElementById("geolocate");
geolocateBtn.addEventListener(
"click",
function () {
geolocation.setTracking(true); // Start position tracking
tileLayer.on("postrender", updateView);
map.render();
disableButtons();
},
false
);
// simulate device move
let simulationData;
const client = new XMLHttpRequest();
client.open("GET", this.$withBase("/data/geolocation-orientation.json"));
/**
* Handle data loading.
*/
client.onload = function () {
simulationData = JSON.parse(client.responseText).data;
};
client.send();
const simulateBtn = document.getElementById("simulate");
simulateBtn.addEventListener(
"click",
function () {
const coordinates = simulationData;
const first = coordinates.shift();
simulatePositionChange(first);
let prevDate = first.timestamp;
function geolocate() {
const position = coordinates.shift();
if (!position) {
return;
}
const newDate = position.timestamp;
simulatePositionChange(position);
window.setTimeout(function () {
prevDate = newDate;
geolocate();
}, (newDate - prevDate) / 0.5);
}
geolocate();
tileLayer.on("postrender", updateView);
map.render();
disableButtons();
},
false
);
function simulatePositionChange(position) {
const coords = position.coords;
geolocation.set("accuracy", coords.accuracy);
geolocation.set("heading", degToRad(coords.heading));
const projectedPosition = fromLonLat([coords.longitude, coords.latitude]);
geolocation.set("position", projectedPosition);
geolocation.set("speed", coords.speed);
geolocation.changed();
}
function disableButtons() {
geolocateBtn.disabled = "disabled";
simulateBtn.disabled = "disabled";
}
},
};
</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
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
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
# 2.GeoTIFF 瓦片金字塔
查看代码详情
<template>
<div ref="map" class="map"></div>
</template>
<script>
export default {
mounted() {
let {
tilegrid: { TileGrid },
Map,
View,
layer: { WebGLTile: WebGLTileLayer },
source: { GeoTIFF, sourcesFromTileGrid },
} = ol;
const tileGrid = new TileGrid({
extent: [-180, -90, 180, 90],
resolutions: [
0.703125, 0.3515625, 0.17578125, 8.7890625e-2, 4.39453125e-2,
],
tileSizes: [
[512, 256],
[1024, 512],
[2048, 1024],
[4096, 2048],
[4096, 4096],
],
});
const pyramid = new WebGLTileLayer({
sources: sourcesFromTileGrid(
tileGrid,
([z, x, y]) =>
new GeoTIFF({
sources: [
{
url: `https://s2downloads.eox.at/demo/EOxCloudless/2019/rgb/${z}/${y}/${x}.tif`,
},
],
})
),
});
const map = new Map({
target: this.$refs.map,
layers: [pyramid],
view: new View({
projection: "EPSG:4326",
center: [12579156, 3274244],
zoom: 0,
showFullExtent: true,
}),
});
},
};
</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
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
# 3.GeoTIFF 与概述
查看代码详情
<template>
<div ref="map" class="map"></div>
</template>
<script>
export default {
mounted() {
let {
Map,
View,
layer: { WebGLTile: TileLayer },
source: { GeoTIFF },
} = ol
// scale values in this range to 0 - 1
const min = 10000
const max = 15000
const base =
"https://landsat-pds.s3.amazonaws.com/c1/L8/139/045/LC08_L1TP_139045_20170304_20170316_01_T1/LC08_L1TP_139045_20170304_20170316_01_T1"
const source = new GeoTIFF({
sources: [
{
url: `${base}_B6.TIF`,
overviews: [`${base}_B6.TIF.ovr`],
min: min,
max: max,
nodata: 0,
},
{
url: `${base}_B5.TIF`,
overviews: [`${base}_B5.TIF.ovr`],
min: min,
max: max,
nodata: 0,
},
{
url: `${base}_B3.TIF`,
overviews: [`${base}_B3.TIF.ovr`],
min: min,
max: max,
nodata: 0,
},
],
})
const map = new Map({
target: this.$refs.map,
layers: [
new TileLayer({
style: {
saturation: -0.3,
},
source: source,
}),
],
view: source.getView(),
})
},
}
</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
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
# 4.GPX 数据
查看代码详情
<template>
<div>
<div ref="map" class="map"></div>
<div id="info"> </div>
</div>
</template>
<script>
export default {
mounted() {
let {
format: { GPX },
Map,
View,
layer: { Tile: TileLayer, Vector: VectorLayer },
source: { XYZ, Vector: VectorSource },
style: { Circle: CircleStyle, Fill, Stroke, Style },
} = 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 raster = new TileLayer({
source: new XYZ({
attributions: attributions,
url:
"https://api.maptiler.com/tiles/satellite/{z}/{x}/{y}.jpg?key=" +
mapkeys.maptiler,
maxZoom: 20,
}),
});
const style = {
Point: new Style({
image: new CircleStyle({
fill: new Fill({
color: "rgba(255,255,0,0.4)",
}),
radius: 5,
stroke: new Stroke({
color: "#ff0",
width: 1,
}),
}),
}),
LineString: new Style({
stroke: new Stroke({
color: "#f00",
width: 3,
}),
}),
MultiLineString: new Style({
stroke: new Stroke({
color: "#0f0",
width: 3,
}),
}),
};
const vector = new VectorLayer({
source: new VectorSource({
url: this.$withBase("/data/gpx/fells_loop.gpx"),
format: new GPX(),
}),
style: function (feature) {
return style[feature.getGeometry().getType()];
},
});
const map = new Map({
layers: [raster, vector],
target: this.$refs.map,
view: new View({
center: [-7916041.528716288, 5228379.045749711],
zoom: 12,
}),
});
const displayFeatureInfo = function (pixel) {
const features = [];
map.forEachFeatureAtPixel(pixel, function (feature) {
features.push(feature);
});
if (features.length > 0) {
const info = [];
let i, ii;
for (i = 0, ii = features.length; i < ii; ++i) {
info.push(features[i].get("desc"));
}
document.getElementById("info").innerHTML =
info.join(", ") || "(unknown)";
map.getTarget().style.cursor = "pointer";
} else {
document.getElementById("info").innerHTML = " ";
map.getTarget().style.cursor = "";
}
};
map.on("pointermove", function (evt) {
if (evt.dragging) {
return;
}
const pixel = map.getEventPixel(evt.originalEvent);
displayFeatureInfo(pixel);
});
map.on("click", function (evt) {
displayFeatureInfo(evt.pixel);
});
},
};
</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
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
# 5.HERE 地图瓦片 API
查看代码详情
<template>
<div>
<div ref="map" class="map"></div>
<select id="layer-select">
<option value="normal.day" selected>Normal Day</option>
<option value="normal.day.transit">Normal Day Transit</option>
<option value="pedestrian.day">Pedestrian Day</option>
<option value="terrain.day">Terrain Day</option>
<option value="satellite.day">Satellite Day</option>
<option value="hybrid.day">Hybrid Day</option>
</select>
</div>
</template>
<script>
export default {
mounted() {
let {
Map,
View,
layer: { Tile: TileLayer },
source: { XYZ },
} = ol;
const apiKey = "Your HERE Maps API key from https://developer.here.com/";
const hereLayers = [
{
base: "base",
type: "maptile",
scheme: "normal.day",
apiKey: apiKey,
},
{
base: "base",
type: "maptile",
scheme: "normal.day.transit",
apiKey: apiKey,
},
{
base: "base",
type: "maptile",
scheme: "pedestrian.day",
apiKey: apiKey,
},
{
base: "aerial",
type: "maptile",
scheme: "terrain.day",
apiKey: apiKey,
},
{
base: "aerial",
type: "maptile",
scheme: "satellite.day",
apiKey: apiKey,
},
{
base: "aerial",
type: "maptile",
scheme: "hybrid.day",
apiKey: apiKey,
},
];
const urlTpl =
"https://{1-4}.{base}.maps.ls.hereapi.com" +
"/{type}/2.1/maptile/newest/{scheme}/{z}/{x}/{y}/256/png" +
"?apiKey={apiKey}";
const layers = [];
let i, ii;
for (i = 0, ii = hereLayers.length; i < ii; ++i) {
const layerDesc = hereLayers[i];
layers.push(
new TileLayer({
visible: false,
preload: Infinity,
source: new XYZ({
url: createUrl(urlTpl, layerDesc),
attributions:
"Map Tiles © " +
new Date().getFullYear() +
" " +
'<a href="https://developer.here.com/" target="_blank">HERE</a>',
}),
})
);
}
const map = new Map({
layers: layers,
target: this.$refs.map,
view: new View({
center: [921371.9389, 6358337.7609],
zoom: 10,
}),
});
function createUrl(tpl, layerDesc) {
return tpl
.replace("{base}", layerDesc.base)
.replace("{type}", layerDesc.type)
.replace("{scheme}", layerDesc.scheme)
.replace("{apiKey}", layerDesc.apiKey);
}
const select = document.getElementById("layer-select");
function onChange() {
const scheme = select.value;
for (let i = 0, ii = layers.length; i < ii; ++i) {
layers[i].setVisible(hereLayers[i].scheme === scheme);
}
}
select.addEventListener("change", onChange);
onChange();
},
};
</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
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
# 6.矢量标签整理
查看代码详情
<template>
<div ref="map" class="map"></div>
</template>
<script>
export default {
mounted() {
let {
format: { GeoJSON },
Map,
View,
layer: { Vector: VectorLayer },
source: { Vector: VectorSource },
style: { Fill, Stroke, Style, Text },
proj: { fromLonLat },
} = ol
const map = new Map({
target: this.$refs.map,
view: new View({
center: fromLonLat([-100, 38.5]),
zoom: 4,
}),
})
const labelStyle = new Style({
text: new Text({
font: "12px Calibri,sans-serif",
overflow: true,
fill: new Fill({
color: "#000",
}),
stroke: new Stroke({
color: "#fff",
width: 3,
}),
}),
})
const countryStyle = new Style({
fill: new Fill({
color: "rgba(255, 255, 255, 0.6)",
}),
stroke: new Stroke({
color: "#319FD3",
width: 1,
}),
})
const style = [countryStyle, labelStyle]
const vectorLayer = new VectorLayer({
background: "white",
source: new VectorSource({
url: "https://openlayers.org/data/vector/us-states.json",
format: new GeoJSON(),
}),
style: function (feature) {
const label = feature.get("name").split(" ").join("\n")
labelStyle.getText().setText(label)
return style
},
declutter: true,
})
map.addLayer(vectorLayer)
},
}
</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