HTML5 網絡拓撲圖整合 OpenLayers 實現 GIS 地圖應用

在前面《百度地圖、ECharts整合HT for Web網絡拓撲圖應用》咱們有介紹百度地圖和 HT for Web 的整合,咱們今天來談談 OpenLayersHT for Web 的整合。html

HT for Web 做爲邏輯拓撲圖形組件自身沒有 GIS 功能,但能夠與各類 GIS 引擎即其客戶端組件進行融合,各取所長實現邏輯拓撲和物理拓撲的無縫融合,本章將具體介紹 HT for Web 與開發免費的 OpenLayers 地圖結合應用的關鍵技術點,該文介紹的結合的原理,其實還可推廣到與 ArcGIS、百度地圖以及 GoogleMap 等衆多 GIS 地圖引擎融合的解決方案。node

Screen-Shot-2014-12-02-at-1.15.33-AM1.png

以上抓圖爲本文介紹的例子最終運行效果,接下來咱們一步步來實現,首選顯示地圖信息須要有城市經緯度數據,搜索了下感謝此篇博客提供的數據。這麼大量的數據我採用的是《HT圖形組件設計之道(四)》中介紹的getRawText函數方式,有了數據以後剩下就是呈現的問題了,咱們須要將HT的GraphView組件與OpenLayers的map地圖組件疊加在一塊兒,也就是OpenLayers的tile地圖圖片在下方,GraphView的組件在上方,因爲GraphView默認是透明的,所以非圖元部分用戶可穿透看到地圖內容。找到合適的組件插入位置是頭疼的事情,ArcGIS、百度地圖包括GoogleMap幾乎每一個不一樣的GIS組件都須要嘗試一番才能找到合適的插入位置,其餘GIS引擎組件的整合之後章節再介紹,本文咱們關注的OpenLayers的插入方式爲map.viewPortDiv.appendChild(graphView.getView())。算法

HT和OpenLayers組件疊加在一塊兒以後,剩下就是拓撲裏面圖元的擺放位置與經緯度結合的問題,常規網絡拓撲圖中存儲在ht.Node圖元的position是邏輯位置,和經緯度沒有任何關係,所以在GIS應用中咱們須要根據圖元的經緯度信息換算出position的屏幕邏輯座標信息,若是你知道投影算法也能夠本身提供函數處理,但全部GIS組件都提供了相似的API函數供調用,固然這部分也沒有標準化,不一樣的GIS組件須要調用的API都有差別,但基本原理是一致的,對於OpenLayers咱們經過map.getPixelFromLonLat(data.lonLat)能夠將經緯度信息轉換成屏幕像素邏輯座標,也就是ht.Node須要的position座標信息。網絡

細心的同窗會想到轉換是雙向的,有可能用戶須要拖動圖元節點改變其經緯度信息,這時候咱們就須要另一個方向函數,即根據屏幕邏輯座標轉換成當前座標對應的經緯度,在OpenLayers中咱們經過map.getLonLatFromPixel(new OpenLayers.Pixel(x, y));能夠搞定。app

顯示搞定後剩下就是交互的問題了,HT本身有套交互體系,OpenLayers也須要地圖漫遊和縮放的交互,二者如何結合呢?若是能保留住二者的功能那就最好了,答案時確定的,咱們只須要添加mousedown或touchstart事件監聽,若是graphView.getDataAt(e)選中了圖元咱們就經過e.stopPropagation();中止事件的傳播,這樣map地圖就不會響應,這時候HT接管了交互,若是沒有選中圖元則map接管地圖操做的交互。dom

以上交互設計彷佛很完美了,結果運行時發現了幾處折騰了我好久才找到解決方案的坑:函數

  1. 設置map.events.fallThrough = true;不然map不會將事件透傳到HT的GraphView組件性能

  2. graphView.getView().style.zIndex = 999; 須要指定必定的zIndex不然會被遮擋this

  3. graphView.getView().className = ‘olScrollable’; 不然滾輪不會響應地圖縮放spa

  4. 設置ht.Default.baseZIndex: 1000 不然ToolTip會被遮擋

爲了讓這個例子用戶體驗更友好,我還用心折騰了些技術點供參考:

  1. 採用開源免費的http://llllll.li/randomColor/隨機顏色類庫,該類庫還有不少很是棒的顏色獲取函數,我只是簡單的爲每一個省份顯示不同的顏色

  2. 重載了isVisible、isNoteVisible和isLabelVisible僅在縮放達到必定級別才顯示更詳細的內容,不然縮小時全部城市信息都顯示徹底沒法查看,多少也能提升顯示性能

如下爲最終效果的抓圖、視頻和源代碼:http://v.youku.com/v_show/id_...

Screen-Shot-2014-12-02-at-1.15.33-AM2.png

Screen-Shot-2014-12-02-at-1.16.18-AM.png

Screen-Shot-2014-12-02-at-1.16.47-AM.png

Screen-Shot-2014-12-02-at-1.17.30-AM.png

function init(){                
    graphView = new ht.graph.GraphView();
    var view = graphView.getView();                 

    map = new OpenLayers.Map("map");
    var ol_wms = new OpenLayers.Layer.WMS(
        "OpenLayers WMS",
        "http://vmap0.tiles.osgeo.org/wms/vmap0",
        {layers: "basic"}
    );
    map.addLayers([ol_wms]);
    map.addControl(new OpenLayers.Control.LayerSwitcher());
    map.zoomToMaxExtent();                
    map.events.fallThrough = true;

    map.zoomToProxy = map.zoomTo;
    map.zoomTo =  function (zoom,xy){
        view.style.opacity = 0;
        map.zoomToProxy(zoom, xy);    
        console.log(zoom);
    };                

    map.events.register("movestart", this, function() {
    });
    map.events.register("move", this, function() {                   
    });
    map.events.register("moveend", this, function() {
        view.style.opacity = 1;
        reset();
    });                

    graphView.getView().className = 'olScrollable';
    graphView.setScrollBarVisible(false);
    graphView.setAutoScrollZone(-1);
    graphView.handleScroll = function(){};
    graphView.handlePinch = function(){};     
    graphView.mi(function(e){
        if(e.kind === 'endMove'){
            graphView.sm().each(function(data){
                if(data instanceof ht.Node){
                   var position = data.getPosition(),
                       x = position.x + graphView.tx(),
                       y = position.y + graphView.ty();  

                   data.lonLat = map.getLonLatFromPixel(new OpenLayers.Pixel(x, y));                                                                     
                }                            
            });
        }
    });
    graphView.enableToolTip();
    graphView.getToolTip = function(event){
        var data = this.getDataAt(event);
        if(data){
            return '城市:' + data.s('note') + '
經度:' + data.lonLat.lon + '
維度:' + data.lonLat.lat;
        }
        return null;
    };
    graphView.isVisible = function(data){
        return map.zoom > 1 || this.isSelected(data);
    };
    graphView.isNoteVisible = function(data){
        return map.zoom > 6 || this.isSelected(data);
    }; 
    graphView.getLabel = function(data){
        return '經度:' + data.lonLat.lon + '\n維度:' + data.lonLat.lat;
    };
    graphView.isLabelVisible = function(data){
        return map.zoom > 7 || this.isSelected(data);
    };                 

    view.addEventListener("ontouchend" in document ? 'touchstart' : 'mousedown', function(e){
        var data = graphView.getDataAt(e);
        if(data || e.metaKey || e.ctrlKey){
            e.stopPropagation();
        }                      
    }, false); 
    view.style.position = 'absolute';
    view.style.top = '0';
    view.style.left = '0';
    view.style.right = '0';
    view.style.bottom = '0';                
    view.style.zIndex = 999;
    map.viewPortDiv.appendChild(view);

    var color = randomColor();
    lines = china.split('\n');
    for(var i=0; i<lines.length; i++) {
        line = lines[i].trim();
        if(line.indexOf('【') === 0){
            //province = line.substring(1, line.length-1);                
            color = randomColor();
        }else{
            var ss = line.split(' ');
            if(ss.length === 3){
                createNode(parseFloat(ss[1].substr(3)), parseFloat(ss[2].substr(3)), ss[0].substr(3), color);                                                      
            }
        }
    }                                
}

function reset(){
    graphView.tx(0);
    graphView.ty(0);
    graphView.dm().each(function(data){                    
        if(data.lonLat){                            
            data.setPosition(map.getPixelFromLonLat(data.lonLat));                           
        }
    });
    graphView.validate();
}

function createNode(lon, lat, name, color){
    var node = new ht.Node();
    node.s({
        'shape': 'circle',
        'shape.background': color,
        'note': name,                    
        'label.background': 'rgba(255, 255, 0, 0.5)',                    
        'select.type': 'circle'
    });
    node.setSize(10, 10);
    var lonLat = new OpenLayers.LonLat(lon, lat);
    lonLat.transform('EPSG:4326', map.getProjectionObject());
    node.setPosition(map.getPixelFromLonLat(lonLat));
    node.lonLat = lonLat;
    graphView.dm().add(node);
    return node;
}
相關文章
相關標籤/搜索