0

我想在 openlayers 的地图上显示全球 UTM 网格。整个系统没有项目定义,只有各个区域。对于我的地图的投影,我想使用 EPSG:3857.Im 使用 Ol-Ext 刻度对地图和网格使用不同的投影,因为这是我的项目中需要的。有没有办法用 ol-ext 刻度线做到这一点,还是我必须开发自定义解决方案?

4

1 回答 1

0

您可以定义一个将所有 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 刻度具有intervals类似于 OpenLayers 刻度中的选项,而不是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>

于 2021-08-16T15:44:52.337 回答