meface/docs/article/gis/openlayers/ol_projection.md

410 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
title: Openlayers投影与坐标转换
date: 2020-12-25
author: ac
sidebar: false
tags:
- Openlayers
categories:
- GIS
---
## 投影
在初始化`map`实例时,如果不在`view`中用`projection`参数指定投影坐标系,则将会使用默认的空间参考`EPSG:3857`(Web 墨卡托投影)。后续叠加的图层,都会将数据进行投影转换,确保`map`在同一个空间参考内。
> `EPSG`(European Petroleum Survey Group欧洲石油调查组织)负责维护并发布坐标参照系统的数据集参数以及坐标转换描述。它将已有的椭球体投影坐标系等及其不同组合都对应着不同的ID号这个号在`EPSG`中被称为`EPSG code`,它代表特定的椭球体、单位、地理坐标系或投影坐标系等信息。
`Openlayers`中的指定空间参考可以直接使用`EPSG code`指定,如`EPSG:4326`,因为`ol.proj.Projection`已经帮我们定义好`EPSG:4326`和`EPSG:3857`这两个空间参考的参数,同时也为这两个空间参考定义了一些别名,如`EPSG:3857`的`EPSG:102100`、`EPSG:102113`、`EPSG:900913`这些曾用名,也有用于识别`GML`要素的` http://www.opengis.net/gml/srs/epsg.xml#3857`和`urn:ogc:def:crs:EPSG:6.18:3:3857`。
空间参考的别名可以使用`proj4js`的`defs()`方法添加:
```javascript
proj4.defs('urn:x-ogc:def:crs:EPSG:4326', proj4.defs('EPSG:4326'));
```
`ol`对于其他空间参考,可以使用`proj4js`进行自定义。`defs()`方法定义,`register()`方法进行注册。
> `proj4js`是一个JavaScript库是一个强大的通用坐标转换引擎可以同时进行大规模地图投影和高精密度的坐标转换。
```javascript
proj4.defs(
'EPSG:21781',
'+proj=somerc +lat_0=46.95240555555556 +lon_0=7.439583333333333 +k_0=1 ' +
'+x_0=600000 +y_0=200000 +ellps=bessel ' +
'+towgs84=660.077,13.551,369.344,2.484,1.783,2.939,5.66 +units=m +no_defs'
);
register(proj4);
```
自定义的投影必须是在[https://epsg.io/](https://epsg.io/)可以找到的才有效。
![image-20201221151526367](./images/image-20201221151526367.png)
![image-20201221151927037](./images/image-20201221151927037.png)
**常用参数列表**
| 参数 | 描述 |
| :-------- | ------------------------------------------------------------ |
| +proj | 投影名称,安装`proj`后可以使用`proj - l `命令查看支持的投影名称 |
| +lat_0 | 维度起点 |
| +lon_0 | 中央经线 |
| +x_0 | 东(伪)偏移量 |
| +y_0 | 北(伪)偏移量 |
| +ellps | 椭球体名称,使用`proj -le` 命令查看支持哪些椭球体 |
| +units | 水平单位meters。使用`proj -lu `命令查看`PROJ`支持的单位 |
| +lat_ts | 有效纬度范围 |
| +a | 椭球体长半轴长度 |
| +b | 椭球体短半轴长度 |
| +k | 比例系数(比例因子),旧版本,不赞成使用 |
| +k_0 | 比例系数(比例因子) |
| +vunits | 垂直单位 |
| +datum | 基准面名称,使用`proj -ld`命令查看支持的基准面 |
| +towgs84 | 3参数或7参数基面转换 |
| +to_meter | 将水平单位转换为米计算输出转换参数1英尺= |
| +no_defs | 不要使用`proj`库中的缺省定义文件。 |
下面使用`OSM`地图作为示例图层,定义`CGCS2000`的空间参考。
```html
<!doctype html>
<html>
<head>
<link rel="stylesheet" href="css/ol.css" type="text/css">
<style>
.map {
height: 85vh;
width: 100%;
}
</style>
<script src="lib/ol.js"></script>
<!--引入proj4.js-->
<script src="lib/proj4.js"></script>
<title>OpenLayers example</title>
<meta charset="UTF-8">
</head>
<body>
<h2>Projection CGCS2000(EPSG:4490)</h2>
<div id="map" class="map"></div>
<script type="text/javascript">
var shenzhen = [113.958334, 22.535640];
//使用proj4.defs()定义投影
proj4.defs("EPSG:4490", "+proj=longlat +ellps=GRS80 +no_defs");
//使proj4中定义的投影在OpenLayers中可用。
ol.proj.proj4.register(proj4);
//创建"EPSG:4490"的Projection实例Openlayers将可以从Peoj4js中获取转换函数。
var cgcs2000 = new ol.proj.Projection({
code: "EPSG:4490",//EPSG code
extent:[-180,-90,180,90],
worldExtent:[-180,-90,180,90],
units:"degrees"//Projection units: `'degrees'`, `'ft'`, `'m'`, `'pixels'`, `'tile-pixels'` or `'us-ft'.
});
ol.proj.addProjection(cgcs2000);
var map = new ol.Map({
target: "map",
layers: [
new ol.layer.Tile({
source: new ol.source.OSM()
})
],
view: new ol.View({
center:ol.proj.fromLonLat(shenzhen,cgcs2000),
zoom: 11,
projection: cgcs2000
})
})
</script>
</body>
</html>
```
可以将瓦片图层`SOM`当做底图,再往上叠加图层。
## 坐标转换
对于矢量数据可以使用`ol.proj`命名空间下的方法,在客户端进行坐标转换,如:
```javascript
//将坐标coordinate从“EPSG:3857”转换到“EPSG:4490”
ol.proj.transform([12686109.88955285,2572810.705991532],"EPSG:3857","EPSG:4490");
//添加坐标转换函数
ol.proj.addCoordinateTransforms("EPSG:3857","EPSG:4490",function (coordinate) {
return proj4("EPSG:3857","EPSG:4490",coordinate);
},function (coordinate) {
return proj4("EPSG:4490","EPSG:3857",coordinate);
})
//将经纬度的coordinate转换到目标空间参考下的坐标默认是EPSG:3857
ol.proj.fromLonLat([113.958334, 22.535640],cgcs2000)
//将coordinate转换成经纬度
ol.proj.toLonLat([12686109.88955285,2572810.705991532])
```
上述是对单个coordinate的转换对于几何要素Geometry及其子类点、线、面等可以使用`ol.geom.Geometry`中的`transform()`方法转换几何对象中的每组坐标。像`GeoJSON`、`KML`、`gpx`类型的文件可以选择在解析器解析完成得到要素集合后,对每个要素进行转化。
示例:
```html
<!doctype html>
<html>
<head>
<link rel="stylesheet" href="css/ol.css" type="text/css">
<style>
.map {
height: 85vh;
width: 100%;
}
</style>
<script src="lib/ol.js"></script>
<!--引入proj4.js-->
<script src="lib/proj4.js"></script>
<title>OpenLayers example</title>
<meta charset="UTF-8">
</head>
<body>
<h2>Projection CGCS2000(EPSG:4490)</h2>
<div id="map" class="map"></div>
<script type="text/javascript">
var shenzhen = [113.958334, 22.535640];
//使用proj4.defs()定义投影
proj4.defs("EPSG:4490", "+proj=longlat +ellps=GRS80 +no_defs");
//使proj4中定义的投影在OpenLayers中可用。
ol.proj.proj4.register(proj4);
//创建"EPSG:4490"的Projection实例Openlayers将可以从Peoj4js中获取转换函数。
var cgcs2000 = new ol.proj.Projection({
code: "EPSG:4490",//EPSG code
extent:[-180,-90,180,90],
worldExtent:[-180,-90,180,90],
units:"degrees"//Projection units: `'degrees'`, `'ft'`, `'m'`, `'pixels'`, `'tile-pixels'` or `'us-ft'.
});
var map = new ol.Map({
target: "map",
layers: [
new ol.layer.Tile({
source: new ol.source.OSM()
})
],
view: new ol.View({
center:ol.proj.fromLonLat(shenzhen,cgcs2000),
zoom: 11,
projection: cgcs2000
})
})
var geojson = new ol.source.Vector();
fetch("data/geojson/test.json").then(function(response){
return response.json();
}).then(function (value) {
var features = (new ol.format.GeoJSON()).readFeatures(value);
for(var i=0;i<features.length;i++){
features[i].getGeometry().transform("EPSG:3857","EPSG:4490");
}
geojson.addFeatures(features);
})
var testLayer = new ol.layer.Vector({
source: geojson
});
map.addLayer(testLayer);
</script>
</body>
</html>
```
![projection4490](./images/projection4490.png)
`test.json`数据文件
```json
{
"type": "FeatureCollection",
"crs": {
"type": "name",
"properties": {
"name": "urn:ogc:def:crs:EPSG:3857"
}
},
"features": [
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Point",
"coordinates": [
12686109.88955285,
2572810.705991532
]
}
},
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Point",
"coordinates": [
12683644.795390654,
2568835.9805207034
]
}
},
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "LineString",
"coordinates": [
[
12673134.704001442,
2577473.364716928
],
[
12671453.089379169,
2571625.932053112
],
[
12689186.479941329,
2580358.8625346934
],
[
12677606.270156125,
2580435.2995629786
]
]
}
}
]
}
```
对于服务类型的数据,像`WFS`、`WMS`服务可以在请求中使用`srsname`或`CRS`参数指定返回要素的空间参考,将坐标转换的工作在服务端完成。
示例:
```html
<!doctype html>
<html >
<head>
<link rel="stylesheet" href="css/ol.css" type="text/css">
<style>
.map {
height: 85vh;
width: 100%;
}
</style>
<script src="lib/ol.js"></script>
<script src="lib/proj4.js"></script>
<title>OpenLayers example</title>
<meta charset="UTF-8">
</head>
<body>
<h2>Projection加载GeoServer的WFS服务</h2>
<div id="map" class="map"></div>
<script type="text/javascript">
//使用proj4.defs()定义投影
proj4.defs("EPSG:4490", "+proj=longlat +ellps=GRS80 +no_defs");
//使proj4中定义的投影在OpenLayers中可用。
ol.proj.proj4.register(proj4);
//创建"EPSG:4490"的Projection实例Openlayers将可以从Peoj4js中获取转换函数。
var cgcs2000 = new ol.proj.Projection({
code: "EPSG:4490",//EPSG code
extent:[-180,-90,180,90],
worldExtent:[-180,-90,180,90],
units:"degrees"//Projection units: `'degrees'`, `'ft'`, `'m'`, `'pixels'`, `'tile-pixels'` or `'us-ft'.
});
var nyc = [-73.92722,40.774221];
var map = new ol.Map({
layers: [
new ol.layer.Tile({
source: new ol.source.OSM()
}),
new ol.layer.Vector({
source: new ol.source.Vector({
format: new ol.format.GeoJSON(),
url: function(extent) {
//直接返回WFS的GetFeature接口访问地址设置outputFormat为json格式和format中的解析器一致
//使用srsname指定返回要素的空间参考srsname=EPSG:4490
return 'http://localhost:8080/geoserver/wfs?service=WFS&' +
'version=1.1.0&request=GetFeature&typename=tiger:tiger_roads&' +
'outputFormat=application/json&srsname=EPSG:4490&' +
'bbox=' + extent.join(',') + ',EPSG:4490';
},
/*
* 加载策略,可选值:
* all一次性加载所有的要素
* bbox加载地图当前视图范围内的要素
* tile基于瓦片格网加载要素
*/
strategy: ol.loadingstrategy.bbox
})
}),
vecLayer
],
target: document.getElementById('map'),
view: new ol.View({
center: nyc,
maxZoom: 19,
zoom: 12,
projection: cgcs2000
})
});
</script>
</body>
</html>
```
![exmaple01](./images/exmaple01.png)
```javascript
var map = new ol.Map({
layers: [
new ol.layer.Tile({
source: new ol.source.OSM()
}),
new ol.layer.Tile({
source: new ol.source.TileWMS({
url: 'http://localhost:8080/geoserver/tiger/ows',
params: {
//WIDTH, HEIGHT, BBOX and CRS (SRS for WMS version < 1.3.0) 将被动态设置.
'LAYERS': 'tiger:tiger_roads',
'TILED': false,
'VERSION':'1.1.0',
'TRANSPARENT':true,//
'CRS':"EPSG:4490"
},
//远程WMS服务器的类型
serverType: 'geoserver',
// 用于渲染的不透明度过渡的持续时间。要禁用不透明过渡设置transition为: 0
transition: 0
})
})
],
target: document.getElementById('map'),
view: new ol.View({
center: nyc,
maxZoom: 19,
zoom: 12,
projection: cgcs2000
})
});
```
![example03](./images/example03.png)