如何在 openlayers 的地图上显示全球 UTM 网格?
Posted
技术标签:
【中文标题】如何在 openlayers 的地图上显示全球 UTM 网格?【英文标题】:How can I display the global UTM grid on a map in openlayers? 【发布时间】:2021-10-18 10:09:37 【问题描述】:我想在 openlayers 的地图上显示全球 UTM 网格。整个系统没有项目定义,只有各个区域。对于我的地图的投影,我想使用 EPSG:3857.Im 使用 Ol-Ext 刻度对地图和网格使用不同的投影,因为这是我的项目中需要的。有没有办法用 ol-ext 刻度线做到这一点,还是我必须开发自定义解决方案?
【问题讨论】:
哪个 UTM 网格?关于部门或公制网格?无论如何:投影不是网格。只需画出相关的线条(图书馆就是这样做的)。 我希望网格看起来像 map.army 上的 UTM 网格。什么是部门和公制网格? 所以,描述它(并通过编辑问题)。注意:没有多少人会打开随机链接(而且“军队”不是官方的,而且这种***域名通常会吸引骗子)。 [对不起。 UTM 使用“区域”而不是“扇区”。但是公制:UTM 提供公制坐标,所以您是否需要一条线,例如一个区域内 100 公里(+ 区域网格,由度数定义)] 【参考方案1】:您可以定义一个将所有 UTM 投影合并为一个的投影(例如,在区域的每次增加时将 x 坐标添加 1000000),同时仍对该区域使用适当的 UTM 变换。
<!doctype html>
<html lang="en">
<head>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io@master/en/v6.6.1/css/ol.css" type="text/css">
<style>
html, body, .map
margin: 0;
padding: 0;
width: 100%;
height: 100%;
</style>
<script src="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io@master/en/v6.6.1/build/ol.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/proj4js/2.7.5/proj4.js"></script>
<link rel="stylesheet" href="https://viglino.github.io/ol-ext/dist/ol-ext.css" type="text/css">
<script src="https://viglino.github.io/ol-ext/dist/ol-ext.js"></script>
</head>
<body>
<div id="map" class="map"></div>
<script type="text/javascript">
const utmProjs = [];
for (let zone = 1; zone <= 60; zone++)
const code = "EPSG:" + (32600 + zone);
proj4.defs(code, "+proj=utm +zone=" + zone + " +ellps=WGS84 +datum=WGS84 +units=m +no_defs");
ol.proj.proj4.register(proj4);
utmProjs[zone] = ol.proj.get(code);
const llProj = ol.proj.get("EPSG:4326");
const midpointX = 500000;
const width = midpointX * 2;
function ll2utm(ll)
const lon = (((ll[0] % 360) + 540) % 360) - 180; // normalise any wrapx
const lat = ll[1];
const zone = Math.floor((180 + lon) / 6) + 1;
const zoneCoord = ol.proj.transform([lon, lat], llProj, utmProjs[zone]);
return [zoneCoord[0] + (zone - 1) * width, zoneCoord[1]];
function utm2ll(coord)
const zone = Math.floor(coord[0] / width) % 60 + 1;
const c0 = coord[0] % width;
const c1 = coord[1];
const ll = ol.proj.transform([c0, c1], utmProjs[zone], llProj);
if (Math.floor((180 + ll[0]) / 6) + 1 != zone)
ll[0] = (zone - (c0 < midpointX ? 1 : 0)) * 6 - 180;
return ll;
const UTM = new ol.proj.Projection(
code: 'GlobalUTM',
units: 'm',
extent: [0, -10000000, 60 * width, 10000000]
);
ol.proj.addProjection(UTM);
ol.proj.addCoordinateTransforms(
llProj,
UTM,
ll2utm,
utm2ll
);
const map = new ol.Map(
target: 'map',
layers: [
new ol.layer.Tile(
source: new ol.source.OSM()
)
],
view: new ol.View(
center: ol.proj.fromLonLat([37.41, 8.82]),
zoom: 4
)
);
const viewProj = map.getView().getProjection();
ol.proj.addCoordinateTransforms(
viewProj,
UTM,
function(coord)
return ll2utm(ol.proj.toLonLat(coord, viewProj));
,
function(coord)
return ol.proj.fromLonLat(utm2ll(coord), viewProj);
,
);
map.addControl(
new ol.control.Graticule(
projection: UTM,
step: 1000,
stepCoord: 1
)
);
</script>
</body>
</html>
如果 ol-ext 刻度有一个类似于 OpenLayers 刻度中的 intervals
选项,而不是使用 step
设置的随机倍数绘制线条
intervals: [1000, 5000, 10000, 50000, 100000, 500000]
可以使区域边界清晰,同时在放大时仍显示细节。如果没有该选项,您可能会使用step
设置为 1000 公里的刻度线来突出显示和标记区域,并使用另一个刻度线step
为 1 公里以获取区域内的详细信息。不幸的是,ol-ext 刻度只是在交叉点之间绘制直线,而不是计算正确的曲线,因此也会产生的 1000 公里 y 值线相对于更准确的 1 公里细节会错位。然而,它可以用于显示标签,但是因为它缺少用于 x 和 y 坐标的单独标签格式化程序,所以需要额外的 x 偏移量才能区分 x 和 y 坐标。在 EPSG:3857 投影中,区域边界线可以很容易地添加到单独的矢量图层中。
<!doctype html>
<html lang="en">
<head>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io@master/en/v6.6.1/css/ol.css" type="text/css">
<style>
html, body, .map
margin: 0;
padding: 0;
width: 100%;
height: 100%;
</style>
<script src="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io@master/en/v6.6.1/build/ol.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/proj4js/2.7.5/proj4.js"></script>
<link rel="stylesheet" href="https://viglino.github.io/ol-ext/dist/ol-ext.css" type="text/css">
<script src="https://viglino.github.io/ol-ext/dist/ol-ext.js"></script>
</head>
<body>
<div id="map" class="map"></div>
<script type="text/javascript">
const utmProjs = [];
for (let zone = 1; zone <= 60; zone++)
const code = "EPSG:" + (32600 + zone);
proj4.defs(code, "+proj=utm +zone=" + zone + " +ellps=WGS84 +datum=WGS84 +units=m +no_defs");
ol.proj.proj4.register(proj4);
utmProjs[zone] = ol.proj.get(code);
const llProj = ol.proj.get("EPSG:4326");
const midpointX = 500000;
const width = midpointX * 2;
const xOffset = 100 * 60 * width;
function ll2utm(ll)
//const world = Math.floor((ll[0] + 180) / 360);
const lon = (((ll[0] % 360) + 540) % 360) - 180; // normalise any wrapx
const lat = ll[1];
const zone = Math.floor((180 + lon) / 6) + 1;
const zoneCoord = ol.proj.transform([lon, lat], llProj, utmProjs[zone]);
const coord = [xOffset + zoneCoord[0] + (zone - 1) * width, zoneCoord[1]];
//coord[0] += world * 60 * width;
return coord;
function utm2ll(coord)
//const world = Math.floor((coord[0] - xOffset) / (60 * width));
const zone = Math.floor(coord[0] / width) % 60 + 1;
const c0 = coord[0] % width;
const c1 = coord[1];
const ll = ol.proj.transform([c0, c1], utmProjs[zone], llProj);
if (Math.floor((180 + ll[0]) / 6) + 1 != zone)
ll[0] = (zone - (c0 < midpointX ? 1 : 0)) * 6 - 180;
//ll[0] += world * 360;
return ll;
const UTM = new ol.proj.Projection(
code: 'GlobalUTM',
units: 'm',
extent: [xOffset, -10000000, xOffset + 60 * width, 10000000],
//global: true
);
ol.proj.addProjection(UTM);
ol.proj.addCoordinateTransforms(
llProj,
UTM,
ll2utm,
utm2ll
);
const map = new ol.Map(
target: 'map',
layers: [
new ol.layer.Tile(
source: new ol.source.OSM()
)
],
view: new ol.View(
center: ol.proj.fromLonLat([37.41, 8.82]),
zoom: 4
)
);
const viewProj = map.getView().getProjection();
ol.proj.addCoordinateTransforms(
viewProj,
UTM,
function(coord)
return ll2utm(ol.proj.toLonLat(coord, viewProj));
,
function(coord)
return ol.proj.fromLonLat(utm2ll(coord), viewProj);
,
);
const features = [
new ol.Feature(
new ol.geom.LineString([[-180, 0], [180, 0]]).transform(llProj, viewProj)
)
];
for (let i = -180; i <= 180; i += 6)
features.push(
new ol.Feature(
new ol.geom.LineString([[i, -85], [i, 85]]).transform(llProj, viewProj)
)
);
;
map.addLayer(
new ol.layer.Vector(
source: new ol.source.Vector(
features: features
),
style: new ol.style.Style(
stroke: new ol.style.Stroke(
color: 'black',
width: 3
),
)
)
);
map.addControl(
new ol.control.Graticule(
projection: UTM,
step: 1000,
stepCoord: 1,
formatCoord: function(coord)
if (coord % width == 0)
return '';
else
return (20000 + (coord % width) / 1000).toString().slice(2);
,
style: new ol.style.Style(
stroke: new ol.style.Stroke(
color: 'black',
width: 1
),
fill: new ol.style.Fill(
color: 'white'
),
text: new ol.style.Text(
stroke: new ol.style.Stroke(
color: 'white',
width: 3
),
fill: new ol.style.Fill(
color: 'black'
),
font: 'bold 12px Arial, Helvetica, Helvetica, sans-serif',
)
)
)
);
map.addControl(
new ol.control.Graticule(
projection: UTM,
step: width,
stepCoord: 1,
formatCoord: function(coord)
if (coord < 0)
return 'S' + (20 + coord / width).toString().slice(1);
else if (coord < xOffset)
return 'N' + (20 + coord / width).toString().slice(1);
else
return 'Z' + (100 + Math.floor(coord / width) % 60 + 1).toString().slice(1);
,
style: new ol.style.Style(
stroke: new ol.style.Stroke(
color: 'transparent',
width: 1
),
fill: new ol.style.Fill(
color: 'transparent'
),
text: new ol.style.Text(
stroke: new ol.style.Stroke(
color: 'white',
width: 3
),
fill: new ol.style.Fill(
color: 'black'
),
font: 'bold 15px Arial, Helvetica, Helvetica, sans-serif',
)
)
)
);
</script>
</body>
</html>
【讨论】:
谢谢!有没有办法突出区域的边界并添加区域编号? 我添加了一个更新,它解决了 ol-ext 刻度的一些问题(特定间隔、正确的曲线以及单独的 x 和 y 轴标签格式),通过复制刻度代码并对其进行自定义。当 180 经线在视图中时,我的变换也存在问题。以上是关于如何在 openlayers 的地图上显示全球 UTM 网格?的主要内容,如果未能解决你的问题,请参考以下文章
如何从业力测试触发 openLayers 3 'singleClick'