# 一.proj
# 1.fromLonLat
查看代码详情
<template>
<div ref="map" class="map"></div>
</template>
<script>
export default {
mounted() {
let {
format: { TopoJSON },
Map,
View,
layer: { VectorTile: VectorTileLayer },
source: { VectorTile: VectorTileSource },
style: { Fill, Stroke, Style },
proj: { fromLonLat },
} = ol
const key = "Your Nextzen API key from https://developers.nextzen.org/"
const roadStyleCache = {}
const roadColor = {
major_road: "#776",
minor_road: "#ccb",
highway: "#f39",
}
const buildingStyle = new Style({
fill: new Fill({
color: "#666",
opacity: 0.4,
}),
stroke: new Stroke({
color: "#444",
width: 1,
}),
})
const waterStyle = new Style({
fill: new Fill({
color: "#9db9e8",
}),
})
const roadStyle = function (feature) {
const kind = feature.get("kind")
const railway = feature.get("railway")
const sort_key = feature.get("sort_key")
const styleKey = kind + "/" + railway + "/" + sort_key
let style = roadStyleCache[styleKey]
if (!style) {
let color, width
if (railway) {
color = "#7de"
width = 1
} else {
color = roadColor[kind]
width = kind == "highway" ? 1.5 : 1
}
style = new Style({
stroke: new Stroke({
color: color,
width: width,
}),
zIndex: sort_key,
})
roadStyleCache[styleKey] = style
}
return style
}
const map = new Map({
layers: [
new VectorTileLayer({
source: new VectorTileSource({
attributions:
"© OpenStreetMap contributors, Who’s On First, " +
"Natural Earth, and openstreetmapdata.com",
format: new TopoJSON({
layerName: "layer",
layers: ["water", "roads", "buildings"],
}),
maxZoom: 19,
url:
"https://tile.nextzen.org/tilezen/vector/v1/all/{z}/{x}/{y}.topojson?api_key=" +
key,
}),
style: function (feature, resolution) {
switch (feature.get("layer")) {
case "water":
return waterStyle
case "roads":
return roadStyle(feature)
case "buildings":
return resolution < 10 ? buildingStyle : null
default:
return null
}
},
}),
],
target: this.$refs.map,
view: new View({
center: fromLonLat([-74.0064, 40.7142]),
maxZoom: 19,
zoom: 15,
}),
})
},
}
</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
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
# 2.使用 EPSG.io 搜索重新投影
查看代码详情
<template>
<div>
<div ref="map" class="map"></div>
<form class="form-inline">
<label for="epsg-query">搜索投影:</label>
<input
type="text"
ref="epsgquery"
placeholder="4326, 27700, 3031, US National Atlas, Swiss, France, ..."
class="form-control"
size="50"
/>
<button ref="epsgsearch" class="btn">搜索</button>
<span ref="epsgresult"></span>
</form>
<form class="form-inline">
<label for="render-edges">
渲染重投影边:
<input type="checkbox" ref="renderedges" />
</label>
<label for="show-tiles">
显示平铺坐标:
<input type="checkbox" ref="showtiles" />
</label>
<label for="show-graticule">
显示分划:
<input type="checkbox" ref="showgraticule" />
</label>
</form>
</div>
</template>
<script>
export default {
mounted() {
let {
extent: { applyTransform },
Map,
View,
layer: { Tile: TileLayer, Graticule },
source: { OSM, Vector: VectorSource, TileDebug },
style: { Stroke },
proj: {
get: getProjection,
getTransform,
proj4: { register },
},
} = ol;
const osmSource = new OSM();
const debugLayer = new TileLayer({
source: new TileDebug({
tileGrid: osmSource.getTileGrid(),
projection: osmSource.getProjection(),
}),
visible: false,
});
const graticule = new Graticule({
strokeStyle: new Stroke({
color: "rgba(255,120,0,0.9)",
width: 2,
lineDash: [0.5, 4],
}),
showLabels: true,
visible: false,
wrapX: false,
});
const map = new Map({
layers: [
new TileLayer({
source: osmSource,
}),
debugLayer,
graticule,
],
target: this.$refs.map,
view: new View({
projection: "EPSG:3857",
center: [12579156, 3274244],
zoom: 1,
}),
});
const resultSpan = this.$refs.epsgresult;
const renderEdgesCheckbox = this.$refs.renderedges;
const showTilesCheckbox = this.$refs.showtiles;
const showGraticuleCheckbox = this.$refs.showgraticule;
function setProjection(code, name, proj4def, bbox) {
if (
code === null ||
name === null ||
proj4def === null ||
bbox === null
) {
resultSpan.innerHTML = "Nothing usable found, using EPSG:3857...";
map.setView(
new View({
projection: "EPSG:3857",
center: [12579156, 3274244],
zoom: 1,
})
);
return;
}
resultSpan.innerHTML = "(" + code + ") " + name;
const newProjCode = "EPSG:" + code;
this.$proj4.defs(newProjCode, proj4def);
register(this.$proj4);
const newProj = getProjection(newProjCode);
const fromLonLat = getTransform("EPSG:4326", newProj);
let worldExtent = [bbox[1], bbox[2], bbox[3], bbox[0]];
newProj.setWorldExtent(worldExtent);
if (bbox[1] > bbox[3]) {
worldExtent = [bbox[1], bbox[2], bbox[3] + 360, bbox[0]];
}
const extent = applyTransform(worldExtent, fromLonLat, undefined, 8);
newProj.setExtent(extent);
const newView = new View({
projection: newProj,
});
map.setView(newView);
newView.fit(extent);
}
function search(query) {
resultSpan.innerHTML = "Searching ...";
fetch("https://epsg.io/?format=json&q=" + query)
.then(function (response) {
return response.json();
})
.then(function (json) {
const results = json["results"];
if (results && results.length > 0) {
for (let i = 0, ii = results.length; i < ii; i++) {
const result = results[i];
if (result) {
const code = result["code"];
const name = result["name"];
const proj4def = result["proj4"];
const bbox = result["bbox"];
if (
code &&
code.length > 0 &&
proj4def &&
proj4def.length > 0 &&
bbox &&
bbox.length == 4
) {
setProjection(code, name, proj4def, bbox);
return;
}
}
}
}
setProjection(null, null, null, null);
});
}
this.$refs.epsgsearch.addEventListener("click", function (event) {
search(this.$refs.epsgquery.value);
event.preventDefault();
});
renderEdgesCheckbox.addEventListener("change", function () {
osmSource.setRenderReprojectionEdges(renderEdgesCheckbox.checked);
});
showTilesCheckbox.addEventListener("change", function () {
debugLayer.setVisible(showTilesCheckbox.checked);
});
showGraticuleCheckbox.addEventListener("change", function () {
graticule.setVisible(showGraticuleCheckbox.checked);
});
},
};
</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
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
# 3.Image 重新投影
查看代码详情
<template>
<div>
<div ref="map" class="map"></div>
<div>
<input type="checkbox" ref="interpolate" checked />
<label for="interpolate">Interpolate</label>
</div>
</div>
</template>
<script>
export default {
mounted() {
let {
Map,
View,
layer: { Tile: TileLayer, Image: ImageLayer },
source: { OSM, ImageStatic: Static },
style: Style,
proj: {
transform,
proj4: { register },
},
extent: { getCenter },
} = ol;
this.$proj4.defs(
"EPSG:27700",
"+proj=tmerc +lat_0=49 +lon_0=-2 +k=0.9996012717 " +
"+x_0=400000 +y_0=-100000 +ellps=airy " +
"+towgs84=446.448,-125.157,542.06,0.15,0.247,0.842,-20.489 " +
"+units=m +no_defs"
);
register(this.$proj4);
const imageExtent = [0, 0, 700000, 1300000];
const imageLayer = new ImageLayer();
const map = new Map({
layers: [
new TileLayer({
source: new OSM(),
}),
imageLayer,
],
target: this.$refs.map,
view: new View({
center: transform(getCenter(imageExtent), "EPSG:27700", "EPSG:3857"),
zoom: 4,
}),
});
const interpolate = this.$refs.interpolate;
function setSource() {
const source = new Static({
url:
"https://upload.wikimedia.org/wikipedia/commons/thumb/1/18/" +
"British_National_Grid.svg/2000px-British_National_Grid.svg.png",
crossOrigin: "",
projection: "EPSG:27700",
imageExtent: imageExtent,
interpolate: interpolate.checked,
});
imageLayer.setSource(source);
}
setSource();
interpolate.addEventListener("change", setSource);
},
};
</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
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
# 4.球体
带有经纬网图层的 Sphere Mollweide 地图
查看代码详情
<template>
<div ref="map" class="map"></div>
</template>
<script>
export default {
mounted() {
let {
format: { GeoJSON },
Map,
View,
layer: { Graticule, Vector: VectorLayer },
source: { OSM, Vector: VectorSource },
style: { Fill, Style },
proj: {
Projection,
proj4: { register },
},
} = ol
this.$proj4.defs(
"ESRI:53009",
"+proj=moll +lon_0=0 +x_0=0 +y_0=0 +a=6371000 " +
"+b=6371000 +units=m +no_defs"
)
register(this.$proj4)
const sphereMollweideProjection = new Projection({
code: "ESRI:53009",
extent: [
-18019909.21177587, -9009954.605703328, 18019909.21177587,
9009954.605703328,
],
worldExtent: [-179, -89.99, 179, 89.99],
})
const style = new Style({
fill: new Fill({
color: "#eeeeee",
}),
})
const map = new Map({
keyboardEventTarget: document,
layers: [
new VectorLayer({
source: new VectorSource({
url: "https://openlayers.org/data/vector/ecoregions.json",
format: new GeoJSON(),
}),
style: function (feature) {
const color = feature.get("COLOR_BIO") || "#eeeeee"
style.getFill().setColor(color)
return style
},
}),
new Graticule(),
],
target: this.$refs.map,
view: new View({
center: [12579156, 3274244],
projection: sphereMollweideProjection,
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
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
# 5.addCoordinateTransforms
查看代码详情
<template>
<div ref="map" class="map"></div>
</template>
<script>
export default {
mounted() {
let {
Map,
View,
layer: { Tile: TileLayer },
source: { TileWMS },
control: { ScaleLine, defaults: defaultControls },
proj: { Projection, addCoordinateTransforms, addProjection, transform },
} = ol
const projection = new Projection({
code: "EPSG:21781",
extent: [485869.5728, 76443.1884, 837076.5648, 299941.7864],
units: "m",
})
addProjection(projection)
addCoordinateTransforms(
"EPSG:4326",
projection,
function (coordinate) {
return [
WGStoCHy(coordinate[1], coordinate[0]),
WGStoCHx(coordinate[1], coordinate[0]),
]
},
function (coordinate) {
return [
CHtoWGSlng(coordinate[0], coordinate[1]),
CHtoWGSlat(coordinate[0], coordinate[1]),
]
}
)
const extent = [420000, 30000, 900000, 350000]
const layers = [
new TileLayer({
extent: extent,
source: new TileWMS({
url: "https://wms.geo.admin.ch/",
crossOrigin: "anonymous",
attributions:
'© <a href="https://shop.swisstopo.admin.ch/en/products/maps/national/lk1000"' +
'target="_blank">Pixelmap 1:1000000 / geo.admin.ch</a>',
params: {
LAYERS: "ch.swisstopo.pixelkarte-farbe-pk1000.noscale",
FORMAT: "image/jpeg",
},
serverType: "mapserver",
}),
}),
new TileLayer({
extent: extent,
source: new TileWMS({
url: "https://wms.geo.admin.ch/",
crossOrigin: "anonymous",
attributions:
'© <a href="https://www.hydrodaten.admin.ch/en/notes-on-the-flood-alert-maps.html"' +
'target="_blank">Flood Alert / geo.admin.ch</a>',
params: { LAYERS: "ch.bafu.hydroweb-warnkarte_national" },
serverType: "mapserver",
}),
}),
]
const map = new Map({
controls: defaultControls().extend([
new ScaleLine({
units: "metric",
}),
]),
layers: layers,
target: this.$refs.map,
view: new View({
projection: projection,
center: transform([8.23, 46.86], "EPSG:4326", "EPSG:21781"),
extent: extent,
zoom: 2,
}),
})
function WGStoCHy(lat, lng) {
lat = DECtoSEX(lat)
lng = DECtoSEX(lng)
lat = DEGtoSEC(lat)
lng = DEGtoSEC(lng)
const lat_aux = (lat - 169028.66) / 10000
const lng_aux = (lng - 26782.5) / 10000
const y =
600072.37 +
211455.93 * lng_aux -
10938.51 * lng_aux * lat_aux -
0.36 * lng_aux * Math.pow(lat_aux, 2) -
44.54 * Math.pow(lng_aux, 3)
return y
}
function WGStoCHx(lat, lng) {
lat = DECtoSEX(lat)
lng = DECtoSEX(lng)
lat = DEGtoSEC(lat)
lng = DEGtoSEC(lng)
const lat_aux = (lat - 169028.66) / 10000
const lng_aux = (lng - 26782.5) / 10000
const x =
200147.07 +
308807.95 * lat_aux +
3745.25 * Math.pow(lng_aux, 2) +
76.63 * Math.pow(lat_aux, 2) -
194.56 * Math.pow(lng_aux, 2) * lat_aux +
119.79 * Math.pow(lat_aux, 3)
return x
}
function CHtoWGSlat(y, x) {
const y_aux = (y - 600000) / 1000000
const x_aux = (x - 200000) / 1000000
let lat =
16.9023892 +
3.238272 * x_aux -
0.270978 * Math.pow(y_aux, 2) -
0.002528 * Math.pow(x_aux, 2) -
0.0447 * Math.pow(y_aux, 2) * x_aux -
0.014 * Math.pow(x_aux, 3)
lat = (lat * 100) / 36
return lat
}
function CHtoWGSlng(y, x) {
const y_aux = (y - 600000) / 1000000
const x_aux = (x - 200000) / 1000000
let lng =
2.6779094 +
4.728982 * y_aux +
0.791484 * y_aux * x_aux +
0.1306 * y_aux * Math.pow(x_aux, 2) -
0.0436 * Math.pow(y_aux, 3)
lng = (lng * 100) / 36
return lng
}
function DECtoSEX(angle) {
const deg = parseInt(angle, 10)
const min = parseInt((angle - deg) * 60, 10)
const sec = ((angle - deg) * 60 - min) * 60
return deg + min / 100 + sec / 10000
}
function DEGtoSEC(angle) {
const deg = parseInt(angle, 10)
let min = parseInt((angle - deg) * 100, 10)
let sec = ((angle - deg) * 100 - min) * 100
const parts = String(angle).split(".")
if (parts.length == 2 && parts[1].length == 2) {
min = Number(parts[1])
sec = 0
}
return sec + min * 60 + deg * 3600
}
},
}
</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
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
# 6.投影和规模
查看代码详情
<template>
<div>
<div ref="map" class="map"></div>
<div>
<label for="view-projection">视图投影</label>
<select ref="viewprojection">
<option value="EPSG:3857">球面墨卡托 (EPSG:3857)</option>
<option value="EPSG:4326" selected>WGS 84 (EPSG:4326)</option>
</select>
</div>
</div>
</template>
<script>
export default {
mounted() {
let {
control: { ScaleLine, defaults: defaultControls },
Map,
View,
layer: { Tile: TileLayer },
source: { OSM },
proj: { getPointResolution, get: getProjection, transform },
} = ol;
const viewProjSelect = this.$refs.viewprojection;
const projection = getProjection(viewProjSelect.value);
const map = new Map({
controls: defaultControls().extend([
new ScaleLine({
units: "metric",
bar: true,
steps: 4,
text: true,
minWidth: 140,
}),
]),
layers: [
new TileLayer({
source: new OSM(),
}),
],
target: this.$refs.map,
view: new View({
center: transform([0, 52], "EPSG:4326", projection),
zoom: 6,
projection: projection,
}),
});
function onChangeProjection() {
const currentView = map.getView();
const currentProjection = currentView.getProjection();
const newProjection = getProjection(viewProjSelect.value);
const currentResolution = currentView.getResolution();
const currentCenter = currentView.getCenter();
const currentRotation = currentView.getRotation();
const newCenter = transform(
currentCenter,
currentProjection,
newProjection
);
const currentPointResolution = getPointResolution(
currentProjection,
1,
currentCenter,
"m"
);
const newPointResolution = getPointResolution(
newProjection,
1,
newCenter,
"m"
);
const newResolution =
(currentResolution * currentPointResolution) / newPointResolution;
const newView = new View({
center: newCenter,
resolution: newResolution,
rotation: currentRotation,
projection: newProjection,
});
map.setView(newView);
}
viewProjSelect.addEventListener("change", onChangeProjection);
},
};
</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