1. 程式人生 > >Openlayers之測量距離與面積

Openlayers之測量距離與面積

1、地圖測量功能

一般的地圖的測量功能主要表現在兩個方面,一是測量距離,一是測量面積;面積的測量是根據滑鼠繪製的範圍,通過地理座標系的轉換而計算出實際面積大小,距離的測量是根據滑鼠在地圖上繪製的點,實時計算出兩點之間的實際距離,下面我們就在Openlayers3中來實現這一功能;

2、程式碼實現

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <title></title>
    <script src="../lib/ol/ol.js"></script>
    <link href="../css/ol.css" rel="stylesheet" />
    <script src="../lib/jquery/jquery-1.8.2.js"></script>
    <link href="../css/bootstrap.min.css" rel="stylesheet" />
    <script src="../lib/bootstrap/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>
3、結果展示

測量距離


測量面積


此外,還能勾選使用大地測量的複選框,進行球面距離和麵積的測量