Openlayers 6.2.1 淺嘗心得(三)

中國有句古話:“授之以魚不如授之以漁”,所以今天我要記錄的主要是關於 OpenLayers 的應用開發思路,而非某一具體的功能或代碼。
OpenLayers 作爲一個開源項目,我們首先需要知道的就是它的 官網源碼地址。官網作爲項目信息的集合地,對開發者來說是一個很好的瞭解項目的入口;而源碼地址則可以幫助我們更好的瞭解代碼邏輯,協助我們定位問題,解決問題。

天才第一步

打開官網,我們會發現對我們有用的部分有三塊:在這裏插入圖片描述

第一部分:導航欄

導航欄有四個模塊:文檔(Docs)、示例(Examples)、接口(API)、源碼(Code)。

  • 文檔頁:爲我們介紹瞭如何入門、常見問題解答和更多問題。實用性不大,四捨五入可以忽略不計。
  • 示例頁:很重要~!如果你是第一次接觸 OpenLayers ,建議把示例頁中的所有示例效果過一遍,好讓我們對其能實現的基本功能有一個簡單的瞭解。同時,示例代碼也是我們在自己後續的項目開發過程中要經常參考的內容。
  • 接口頁:重中之重~!!!各位程序員老哥,我想 API 的重要性就不用我說了吧。
  • 源碼頁:點擊直接跳轉至項目的 GitHub 地址。源碼於博主而言,可以幫我更好的瞭解功能的實現思路,且在實際開發過程中,能夠很好的幫我定位和解決部分疑難雜症。

第二部分:Hello World

首頁的第二部分爲我們留下了幾個鏈接,通過他們我們很容易開始自己的開發。

Quick Start

這部分爲快速開始,通常情況下是沒有實用意義的,就像 Hello World 一樣。打開頁面,我們可以看到用傳統方式搭建項目的示例。(這裏不太推薦這種項目構建方式,因爲如果真的使用這種非官方示例中的方式,會導致我們在後續的開發過程中遇到問題或開發新功能時,不容易調試或借鑑別人的方法)

Tutorials

這個部分爲指南,點擊鏈接後,我們可以看到四個部分:

  • 構建一個OpenLayers應用程序(介紹瞭如何在 Node 環境下構建一個 OpenLayers 應用程序,這是我們正確入門姿勢)
  • 基本概念
  • OpenLayers的一些背景
  • 柵格投影

Workshop

這部分爲一個爲詳細教程,點擊鏈接後選擇自己喜歡的語言(能看懂英文就不錯了~!)進行更加系統的瞭解和學習。

第三部分:迭代史

第三部分,這裏羅列了 OpenLayers 從第二版到第五版的相關內容和地址,爲使用之前版本的老鐵提供相關文檔幫助。

甩代碼

幹聊了半天,是時候來的實際的了。以下代碼爲博主自己封裝的部分功能函數,裏面包含了:

  • 座標系設置
  • 基礎矢量圖層對象的創建與加載
  • 自定義圓形特徵層
  • 圓形特徵層的樣式設置
  • 圓形特徵層的批量創建
  • 圓形圖層的創建
  • 圓圖層的動態添加
  • 點特徵層的創建
  • 點特徵層的樣式設置
  • 點特徵層的批量創建
  • 點圖層的創建
  • 點圖層的動態添加
  • Icon 選中高亮
  • overlay 圖層創建(用於 Icon 鼠標懸浮事件效果)
  • 地圖創建
  • 鼠標 hover 事件
  • 鼠標點擊事件
  • 動態清除已渲染圖層

olFun.js

// 導入 Openlayers 樣式
import 'ol/ol.css';
// 導入 Icon 使用的圖片
import dot_png from "../img/dot.png";
// 導入 Openlayers 模塊
import {get, transform, fromLonLat} from 'ol/proj';
import Map from 'ol/Map'
import View from "ol/View";
import Feature from 'ol/Feature';
import {Tile as TileLayer, Vector as VectorLayer} from 'ol/layer';
import TileWMS from 'ol/source/TileWMS';
import VectorSource from 'ol/source/Vector';
import Circle from "ol/geom/Circle";
import Point from 'ol/geom/Point';
import {Icon, Style, Fill, Stroke} from 'ol/style';
import Text from 'ol/style/Text';
import Overlay from 'ol/Overlay';
//  導入自定義函數
import {stationInfoLayers} from "./calledFunctions";
import {postAjax, replayEvent} from "./originFunctions";

// 創建墨卡託投影座標系
const projection = get("EPSG:3857");
const vectorLayerArr = new Array();

// 創建基礎矢量圖層
const baseLayer = new TileLayer({
    source: new TileWMS({
        url: 'http://127.0.0.1:8080/earthview/services/Map/MapService/WMS',
        params: {LAYERS: 'Map', CRS: projection},
        projection: projection
    })
});
baseLayer.set("name", "BasicLayer", true);

// 創建圓形特徵層
let createCircleFeature = function(coordinate, radius, name){
    if (!coordinate && !radius && !name){
        return null;
    }
    return new Feature({
        geometry: new Circle(transform(coordinate, "EPSG:4326", "EPSG:3857"), radius, "XY"),
        name: name
    })
};
// 創建圓形特徵層樣式
let createCircleStyle = function(color){
    if (!color){
        return null;
    }
    return new Style({
        // 設置填充顏色
        fill: new Fill({
            // 支持 CSS 中其他顏色設置方式 eg: 十六進制; rgb; rgba
            color: color
        })
    })
};
// 創建待渲染的點特徵層數組
let createCircleFeatures = function(circles){
    let circleFeatures = [];
    $.each(circles, function (idx, item) {
        let circleFeature = createCircleFeature(item.coordinate, (item.radius)+10000, "circle" + idx);
        circleFeature.setStyle(createCircleStyle(item.color));
        circleFeatures.push(circleFeature);
    });
    return circleFeatures;
};
// 創建矢量圓圖層
let createCircleVectorLayer = function(features){
    let circleLayer = new VectorLayer({
            source: new VectorSource({
                features: features
            }),
            opacity: 0.5
        });
    circleLayer.set("name", "CircleLayer", true);
    vectorLayerArr.push(circleLayer);
    map.addLayer(circleLayer);
};
// 渲染圓圖層
let renderCircleLayer = function(circles){
    let circleFeatures = createCircleFeatures(circles);
    createCircleVectorLayer(circleFeatures);
};

// 創建點特徵層
let createPointFeature  = function(coordinate, name){
    if (!coordinate && !name){
        return null;
    }
    return new Feature({
        geometry: new Point(fromLonLat(coordinate)),
        name: name
    })
};
// 創建點特徵層樣式
let createPointStyle = function(color, text, scale){
    let pointStyle = new Style({
        image: new Icon({
            color: color,
            crossOrigin: 'anonymous',
            // 對靜態資源的引用需要引入,否則將無法把 js 中用到的圖片編譯至 dist 中
            src: dot_png,
            scale: scale
        })
    });
    // 設置點註記名稱及其樣式
    if(text){
        pointStyle.setText(new Text({
            text: text,
            textAlign: 'left',
            offsetX: 10,
            // 字體設置支持 CSS 通用字體設置方式
            font: "bold 20px 黑體,SimHei",
            fill: new Fill({
                color: color
            }),
            stroke: new Stroke({
                color: "#000000",
                width: 2
            })
        }))
    }
    return pointStyle;
};
// 創建待渲染的點特徵層數組
let createPointFeatures = function(points){
    let pointFeatures = [];
    $.each(points, function (idx, item){
        let feature = createPointFeature([item.lon, item.lat], item.name);
        feature.setStyle(createPointStyle(item.color, item.text, item.scale));
        feature.setId(item.evid);
        feature.set("intensity", item.intensity, true);
        pointFeatures.push(feature);
    });
    return pointFeatures;
};
// 創建矢量點圖層
let createPointsVectorLayer = function(features){
    let pointLayer = new VectorLayer({
        source: new VectorSource({
            features: features
        })
    });
    pointLayer.set("name", "PointLayer", true);
    pointLayer.setZIndex(1);
    vectorLayerArr.push(pointLayer);
    map.addLayer(pointLayer);
};
// 渲染點圖層
let renderPointLayer = function (points) {
    let pointFeatures =  createPointFeatures(points);
    createPointsVectorLayer(pointFeatures);
};

// 這個函數有點牛逼——用來創建並渲染 Icon 的反色 Canvas 圖片
let createAndRenderIconCanvasOverlay = function (feature) {
    if (feature) {
        var image = feature.getStyle().getImage().getImage();
        var canvas = document.createElement('canvas');
        var context = canvas.getContext('2d');
        canvas.width = image.width;
        canvas.height = image.height;
        context.drawImage(image, 0, 0, image.width, image.height);
        var imageData = context.getImageData(0, 0, canvas.width, canvas.height);
        var data = imageData.data;
        for (var i = 0, ii = data.length; i < ii; i = i + (i % 4 == 2 ? 2 : 1)) {
            data[i] = 255 - data[i];
        }
        context.putImageData(imageData, 0, 0);
        feature.setStyle(new Style({
            image: new Icon({
                crossOrigin: 'anonymous',
                src: undefined,
                img: canvas,
                imgSize: canvas ? [canvas.width, canvas.height] : undefined,
            })
        }));
    }
};

// 創建 view
const view = new View({
    center: fromLonLat([105.14805, 35.26971]),
    projection: projection,
    zoom: 5,
    maxZoom: 14,
    minZoom: 4
});
// 創建 overlay
let element = document.getElementById('stationNamePopup');
let popup = new Overlay({
    element: element,
    positioning: 'bottom-center',
    stopEvent: false,
    offset: [0, -10]
});
// 創建 Map
const map = new Map({
    layers: [baseLayer],
    target: "sceneControlDiv",
    view: view,
    overlays: [popup],
    controls: []
});

// 鼠標 hover 事件
map.on('pointermove', function(evt) {
    let feature = map.forEachFeatureAtPixel(evt.pixel,
        function(feature) {
            return feature;
        });
    if (feature) {
        let coordinates = feature.getGeometry().getCoordinates();
        popup.setPosition(coordinates);
        let featureName = feature.get('name');
        if(featureName){
            element.innerHTML = `<div class="stationNameStyle">`+feature.get('name')+`</div>`;
        }
    } else {
        element.innerHTML = "";
    }
});

// 鼠標 click 事件
map.on('click', function(evt) {
    let feature = map.forEachFeatureAtPixel(evt.pixel,
        function(feature) {
            return feature;
        });
    if (feature) {
        let flag = feature.getId().split("_");
        // ↓↓↓↓↓↓↓↓↓↓----------  普通信息彈窗頁面  ----------↓↓↓↓↓↓↓↓↓↓
        if (flag.length < 2) {
            layui.layer.closeAll('tips');
            stationInfoLayers(feature.getId(), feature.get('intensity'));
        } else if("1" === flag[0]){
            // ↓↓↓↓↓↓↓↓↓↓----------  事件回放  ----------↓↓↓↓↓↓↓↓↓↓
            let stations = postAjax("eventStation/selectByDateAndSource", false, 
                {"source":flag[1],"date":flag[2]});
            if(stations.length > 0){
                replayEvent(stations, flag[2], flag[1]);
            }
        }
    }
});

// 清除已渲染圖層
function destoryVectorLayer(layerName) {
        let maxIndex = vectorLayerArr.length;
        for (let i = 0; i < maxIndex; i++) {
            let vectorLayer = vectorLayerArr.pop();
            if("PointLayer" === layerName && "PointLayer" === vectorLayer.get("name")){
                map.removeLayer(vectorLayer);
                break;
            }else if("CircleLayer" === layerName && "CircleLayer" === vectorLayer.get("name")) {
                map.removeLayer(vectorLayer);
                break;
            }else{
                map.removeLayer(vectorLayer);
            }
        }
}

export {map, renderPointLayer, renderCircleLayer, destoryVectorLayer, createAndRenderIconCanvasOverlay}  

注意:

  1. Icon 對象的如果需要引用本地的圖片,必須先通過 import 導入纔行,否則圖片樣式會找不到。導入方式詳見代碼第四行。
  2. 一定、一定、一定要充分利用官方示例和 API ~!!!
  3. 本文使用的座標系爲 Web_Mercator 這樣方便繪製不定半徑(單位:米)的圓,如果使用 WGS84 座標系在繪製不定半徑圓的時候,會因半徑參數變爲弧度增加繪製難度。
  4. 這裏 有另外一些博主在開發過程中編寫的代碼包括 Quick Start 中介紹的非 Node 方式創建項目、加載天地圖數據、在 WGS84 座標系的地圖中以米爲單位繪製圓等。

有道無術,術尚可求;有術無道,止於術。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章