1. 程式人生 > 實用技巧 >openlayers測長度面積 Demo(可直接執行)

openlayers測長度面積 Demo(可直接執行)

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">

<head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  <title></title>
  <link rel="stylesheet" href="https://cdn.bootcss.com/openlayers/4.6.5/ol.css" type="text/css">
  <script src="https://cdn.bootcss.com/openlayers/4.6.5/ol.js"></script>
  <link href="https://cdn.bootcss.com/bootstrap/4.1.1/css/bootstrap.min.css" rel="stylesheet">
  <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
  <script src="https://cdn.bootcss.com/bootstrap/4.1.1/js/bootstrap.min.js"></script>
  <style type="text/css">
    #map {
      width: 100%;
      height: 100%;
      position: absolute;
    }

    #menu {
      float: left;
      position: absolute;
      bottom: 10px;
      left: 10px;
      z-index: 2000;
    }

    .checkbox {
      left: 20px;
    }

    /**
        * 提示框的樣式資訊
        */
    .tooltip {
      position: relative;
      background: rgba(0, 0, 0, 0.5);
      border-radius: 4px;
      color: white;
      padding: 4px 8px;
      opacity: 0.7;
      white-space: nowrap;
    }

    .tooltip-measure {
      opacity: 1;
      font-weight: bold;
    }

    .tooltip-static {
      background-color: #ffffff;
      color: black;
      border: 1px solid white;
    }

    .tooltip-measure:before,
    .tooltip-static:before {
      border-top: 6px solid rgba(0, 0, 0, 0.5);
      border-right: 6px solid transparent;
      border-left: 6px solid transparent;
      content: "";
      position: absolute;
      bottom: -6px;
      margin-left: -7px;
      left: 50%;
    }

    .tooltip-static:before {
      border-top-color: #ffffff;
    }

    #scalebar {
      float: left;
      margin-bottom: 10px;
    }
  </style>
  <script type="text/javascript">
    $(function () {
      //初始化地圖
      var map = new ol.Map({
        target: 'map',
        layers: [
          new ol.layer.Tile({
            source: new ol.source.OSM()
          })
        ],
        view: new ol.View({
          center: new ol.proj.fromLonLat([114.4250, 23.0890]),
          zoom: 18,
          maxZoom: 20
        })
      });

      //定義向量資料來源
      var source = new ol.source.Vector();
      //定義向量圖層
      var vector = new ol.layer.Vector({
        source: source,
        style: new ol.style.Style({
          fill: new ol.style.Fill({
            color: 'rgba(255,255,255,0.2)'
          }),
          stroke: new ol.style.Stroke({
            color: '#e21e0a',
            width: 2
          }),
          image: new ol.style.Circle({
            radius: 5,
            fill: new ol.style.Fill({
              color: '#ffcc33'
            })
          })
        })
      });
      //將向量圖層新增到地圖中
      map.addLayer(vector);

      //新增比例尺控制元件
      var scaleLineControl = new ol.control.ScaleLine({
        units: 'metric',
        target: 'scalebar',
        className: 'ol-scale-line'
      });
      map.addControl(scaleLineControl);


      //建立一個WGS84球體物件
      var wgs84Sphere = new ol.Sphere(6378137);
      //建立一個當前要繪製的物件
      var sketch = new ol.Feature();
      //建立一個幫助提示框物件
      var helpTooltipElement;
      //建立一個幫助提示資訊物件
      var helpTooltip;
      //建立一個測量提示框物件
      var measureTooltipElement;
      //建立一個測量提示資訊物件
      var measureTooltip;
      //繼續繪製多邊形的提示資訊
      var continuePolygonMsg = 'Click to continue drawing the polygon';
      //繼續繪製線段的提示資訊
      var continueLineMsg = 'Click to continue drawing the line';

      //滑鼠移動觸發的函式
      var pointerMoveHandler = function (evt) {
        //Indicates if the map is currently being dragged. 
        //Only set for POINTERDRAG and POINTERMOVE events. Default is false.
        //如果是平移地圖則直接結束
        if (evt.dragging) {
          return;
        }
        //幫助提示資訊
        var helpMsg = 'Click to start drawing';

        if (sketch) {
          //Get the feature's default geometry. 
          //A feature may have any number of named geometries.
          //獲取繪圖物件的幾何要素
          var geom = sketch.getGeometry();
          //如果當前繪製的幾何要素是多邊形,則將繪製提示資訊設定為多邊形繪製提示資訊
          //如果當前繪製的幾何要素是多線段,則將繪製提示資訊設定為多線段繪製提示資訊
          if (geom instanceof ol.geom.Polygon) {
            helpMsg = continuePolygonMsg;
          } else if (geom instanceof ol.geom.LineString) {
            helpMsg = continueLineMsg;
          }
        }
        //設定幫助提示要素的內標籤為幫助提示資訊
        helpTooltipElement.innerHTML = helpMsg;
        //設定幫助提示資訊的位置
        //The coordinate in view projection corresponding to the original browser event.
        helpTooltip.setPosition(evt.coordinate);
        //移除幫助提示要素的隱藏樣式
        $(helpTooltipElement).removeClass('hidden');
      };

      //觸發pointermove事件
      map.on('pointermove', pointerMoveHandler);

      //當滑鼠移除地圖檢視的時為幫助提示要素新增隱藏樣式
      $(map.getViewport()).on('mouseout', function () {
        $(helpTooltipElement).addClass('hidden');
      });

      //獲取大地測量複選框
      var geodesicCheckbox = document.getElementById('geodesic');
      //獲取型別
      var typeSelect = document.getElementById('type');
      //定義一個互動式繪圖物件
      var draw;

      //新增互動式繪圖物件的函式
      function addInteraction() {
        // 獲取當前選擇的繪製型別
        var type = typeSelect.value == 'area' ? 'Polygon' : 'LineString';
        //建立一個互動式繪圖物件
        draw = new ol.interaction.Draw({
          //繪製的資料來源
          source: source,
          //繪製型別
          type: type,
          //樣式
          style: new ol.style.Style({
            fill: new ol.style.Fill({
              color: 'rgba(255,255,255,0.2)'
            }),
            stroke: new ol.style.Stroke({
              color: 'rgba(0,0,0,0.5)',
              lineDash: [10, 10],
              width: 2
            }),
            image: new ol.style.Circle({
              radius: 5,
              stroke: new ol.style.Stroke({
                color: 'rgba(0,0,0,0.7)'
              }),
              fill: new ol.style.Fill({
                color: 'rgba(255,255,255,0.2)'
              })
            })
          })
        });
        //將互動繪圖物件新增到地圖中
        map.addInteraction(draw);

        //建立測量提示框
        createMeasureTooltip();
        //建立幫助提示框
        createHelpTooltip();

        //定義一個事件監聽
        var listener;
        //定義一個控制滑鼠點選次數的變數
        var count = 0;
        //繪製開始事件
        draw.on('drawstart', function (evt) {
          //The feature being drawn.
          sketch = evt.feature;
          //提示框的座標
          var tooltipCoord = evt.coordinate;
          //監聽幾何要素的change事件
          //Increases the revision counter and dispatches a 'change' event.

          listener = sketch.getGeometry().on('change', function (evt) {
            //The event target.
            //獲取繪製的幾何物件
            var geom = evt.target;
            //定義一個輸出物件,用於記錄面積和長度
            var output;
            if (geom instanceof ol.geom.Polygon) {
              map.removeEventListener('singleclick');
              map.removeEventListener('dblclick');
              //輸出多邊形的面積
              output = formatArea(geom);
              //Return an interior point of the polygon.
              //獲取多變形內部點的座標
              tooltipCoord = geom.getInteriorPoint().getCoordinates();
            } else if (geom instanceof ol.geom.LineString) {
              //輸出多線段的長度
              output = formatLength(geom);
              //Return the last coordinate of the geometry.
              //獲取多線段的最後一個點的座標
              tooltipCoord = geom.getLastCoordinate();
            }

            //設定測量提示框的內標籤為最終輸出結果
            measureTooltipElement.innerHTML = output;
            //設定測量提示資訊的位置座標
            measureTooltip.setPosition(tooltipCoord);
          });

          //地圖單擊事件
          map.on('singleclick', function (evt) {
            //設定測量提示資訊的位置座標,用來確定滑鼠點選後測量提示框的位置
            measureTooltip.setPosition(evt.coordinate);
            //如果是第一次點選,則設定測量提示框的文字內容為起點
            if (count == 0) {
              measureTooltipElement.innerHTML = "起點";
            }
            //根據滑鼠點選位置生成一個點
            var point = new ol.geom.Point(evt.coordinate);
            //將該點要素新增到向量資料來源中
            source.addFeature(new ol.Feature(point));
            //更改測量提示框的樣式,使測量提示框可見
            measureTooltipElement.className = 'tooltip tooltip-static';
            //建立測量提示框
            createMeasureTooltip();
            //點選次數增加
            count++;
          });

          //地圖雙擊事件
          map.on('dblclick', function (evt) {
            //根據
            var point = new ol.geom.Point(evt.coordinate);
            source.addFeature(new ol.Feature(point));
          });
        }, this);
        //繪製結束事件
        draw.on('drawend', function (evt) {
          count = 0;
          //設定測量提示框的樣式
          measureTooltipElement.className = 'tooltip tooltip-static';
          //Set the offset for this overlay.
          //設定偏移量
          measureTooltip.setOffset([0, -7]);
          //清空繪製要素
          sketch = null;
          //清空測量提示要素
          measureTooltipElement = null;
          //建立測量提示框
          createMeasureTooltip();
          //Removes an event listener using the key returned by on() or once().
          //移除事件監聽
          ol.Observable.unByKey(listener);
          //移除地圖單擊事件
          map.removeEventListener('singleclick');
        }, this);
      }
      //建立幫助提示框
      function createHelpTooltip() {
        //如果已經存在幫助提示框則移除
        if (helpTooltipElement) {
          helpTooltipElement.parentNode.removeChild(helpTooltipElement);
        }
        //建立幫助提示要素的div
        helpTooltipElement = document.createElement('div');
        //設定幫助提示要素的樣式
        helpTooltipElement.className = 'tooltip hidden';
        //建立一個幫助提示的覆蓋標註
        helpTooltip = new ol.Overlay({
          element: helpTooltipElement,
          offset: [15, 0],
          positioning: 'center-left'
        });
        //將幫助提示的覆蓋標註新增到地圖中
        map.addOverlay(helpTooltip);
      }
      //建立測量提示框
      function createMeasureTooltip() {
        //建立測量提示框的div
        measureTooltipElement = document.createElement('div');
        measureTooltipElement.setAttribute('id', 'lengthLabel');
        //設定測量提示要素的樣式
        measureTooltipElement.className = 'tooltip tooltip-measure';
        //建立一個測量提示的覆蓋標註
        measureTooltip = new ol.Overlay({
          element: measureTooltipElement,
          offset: [0, -15],
          positioning: 'bottom-center'
        });
        //將測量提示的覆蓋標註新增到地圖中
        map.addOverlay(measureTooltip);
      }
      //測量型別發生改變時觸發事件
      typeSelect.onchange = function () {
        //移除之前的繪製物件
        map.removeInteraction(draw);
        //重新進行繪製
        addInteraction();
      };

      //格式化測量長度
      var formatLength = function (line) {
        //定義長度變數
        var length;
        //如果大地測量複選框被勾選,則計算球面距離
        if (geodesicCheckbox.checked) {
          //Return the coordinates of the linestring.
          //獲取座標串
          var coordinates = line.getCoordinates();
          //初始長度為0
          length = 0;
          //獲取源資料的座標系
          var sourceProj = map.getView().getProjection();
          //進行點的座標轉換
          for (var i = 0; i < coordinates.length - 1; i++) {
            //第一個點
            var c1 = ol.proj.transform(coordinates[i], sourceProj, 'EPSG:4326');
            //第二個點
            var c2 = ol.proj.transform(coordinates[i + 1], sourceProj, 'EPSG:4326');
            //獲取轉換後的球面距離
            //Returns the distance from c1 to c2 using the haversine formula.
            length += wgs84Sphere.haversineDistance(c1, c2);
          }
        } else {
          //Return the length of the linestring on projected plane.
          //計算平面距離
          length = Math.round(line.getLength() * 100) / 100;
        }
        //定義輸出變數
        var output;
        //如果長度大於1000,則使用km單位,否則使用m單位
        if (length > 1000) {
          output = (Math.round(length / 1000 * 100) / 100) + ' ' + 'km'; //換算成KM單位
        } else {
          output = (Math.round(length * 100) / 100) + ' ' + 'm'; //m為單位
        }
        return output;
      };

      //格式化測量面積
      var formatArea = function (polygon) {
        //定義面積變數
        var area;
        //如果大地測量複選框被勾選,則計算球面面積
        if (geodesicCheckbox.checked) {
          //獲取初始座標系
          var sourceProj = map.getView().getProjection();
          //Make a complete copy of the geometry.
          //Transform each coordinate of the geometry from one coordinate reference system to another. 
          //The geometry is modified in place. For example, a line will be transformed to a line and a circle to a circle.
          //If you do not want the geometry modified in place, first clone() it and then use this function on the clone.
          //克隆該幾何物件然後轉換座標系
          var geom = polygon.clone().transform(sourceProj, 'EPSG:4326');
          //Return the Nth linear ring of the polygon geometry. 
          //Return null if the given index is out of range. 
          //The exterior linear ring is available at index 0 and the interior rings at index 1 and beyond.
          //獲取多邊形的座標系
          var coordinates = geom.getLinearRing(0).getCoordinates();
          //Returns the geodesic area for a list of coordinates.
          //獲取球面面積
          area = Math.abs(wgs84Sphere.geodesicArea(coordinates));
        } else {
          //獲取平面面積
          area = polygon.getArea();
        }
        //定義輸出變數
        var output;
        //當面積大於10000時,轉換為平方千米,否則為平方米
        if (area > 10000) {
          output = (Math.round(area / 1000000 * 100) / 100) + ' ' + 'km<sup>2</sup>';
        } else {
          output = (Math.round(area * 100) / 100) + ' ' + 'm<sup>2</sup>';
        }
        return output;
      };
      //新增互動繪圖物件
      addInteraction();
    });
  </script>
</head>

<body>
  <div id="map">
    <div id="menu">
      <label>測量型別選擇</label>
      <select id="type">
        <option value="length">長度</option>
        <option value="area">面積</option>
      </select>
      <label class="checkbox"><input type="checkbox" id="geodesic" />使用大地測量</label>
    </div>
  </div>
  <div id="scalebar"></div>
</body>

</html>