百度Map與HT for Web結合的GIS網絡拓撲應用

在《HT for Web整合OpenLayers實現GIS地圖應用》篇中介紹了HT for Web與OpenLayers的整合,很多朋友反應國內用得比較多的仍是百度地圖,雖然HT整合百度地圖原理與OpenLayers一致,但不一樣GIS引擎客戶端結合代碼細節仍是有很多差別,自定義地圖風格更是徹底不同,爲此我再開篇介紹下HT與百度地圖整合的方案,此次咱們將改進之前的例子,除了表明城市的拓撲節點外,再增長連線鏈接省會和城市,實現網絡拓撲鏈路的流動效果。php

Screen Shot 2014-12-03 at 11.18.02 PM

百度地圖有多種客戶端SDK,咱們這裏用的天然是JavaScript版的API,百度地圖的2.0版增長了很多新功能,例如能夠自定義地圖樣式模板,本例中咱們特地設置成style:’midnight’的深色背景風格。插入map的位置與OpenLayers也不同,經過mapDiv.firstChild.firstChild.appendChild(view);插入,zIndex這些屬性都還好不須要設置。html

座標轉換方面從經緯度轉換成平面座標是map.pointToPixel函數,經過node.setPosition(map.pointToPixel(new BMap.Point(lon, lat)));可設置ht.Node對應的平面邏輯座標,經過map.pixelToPoint(new BMap.Pixel(x,y))可將平面座標轉換成經緯度座標,咱們在監聽節點圖元被拖拽結束的endMove須要從新計算當前位置的經緯度時用到。node

地圖數據方面每一個省的省會城市都是第一個出現,所以咱們構建了size大一點的帶漸進色的圖元表明省會城市,其餘城市構建時同時構建ht.Edge的連線與省會節點鏈接,同時引入HTht-flow.js插件,只須要graphView.enableFlow(60);一句話就能夠啓動流動連線功能,每條edge連線還能夠有不少flow.*相關的流動參考可控制流動效果,這裏的簡單例子咱們不須要作定製,保持HT默認的流動參數就足夠酷了。數組

另外經過graphView.setLayers(['edgeLayer', 'nodeLayer']);咱們爲拓撲設置了兩個層,node.setLayer(‘nodeLayer’);和edge.setLayer(‘edgeLayer’)使得圖元節點所有呈現於連線之上,這個僅是簡單例子若是須要能夠隨意定義更多的layers,layers的數組順序決定了最終圖元顯示的前後順序。網絡

傳統使用GIS應用來講圖層都須要操做GIS客戶端API來進行,但從這個例子你們能夠發現,之前須要在GIS的SDK擴展的功能也能夠經過在HTGraphView實現,這樣地圖僅僅是做爲背景,業務邏輯代碼徹底在更靈活強大的HT模型中進行,例如圖元的隱藏和顯示也都在HTGraphView中重載了graphView.isVisible函數實現的,經過HT設置能夠定義到更細節的isNoteVisible這樣的圖元部件是否可見,這裏代碼和OpenLayers有的區別的僅僅是從map.zoom改成map.getZoom()。app

另外由於城市節點較多,每次移動界面時咱們都須要調用node.setPosition(map.pointToPixel(new BMap.Point(lon, lat)));從新定位圖元平面座標,畢竟對於js來講密集型的大量計算不是其強項,所以咱們監聽了map的movestart、moveend、zoomstart、zoomend、dragstart和dragend等事件,作的都是一樣的事情在開始變化前經過view.style.opacity = 0;所以GraphView拓撲圖,當事件結束後經過view.style.opacity = 1;再次顯示拓撲圖,同時調用resetPosition();從新定位圖元節點,這樣保證用戶可以流暢的操做地圖,而密集型的運算僅在最後才進行一次。框架

拓撲圖方面還有些細節,例如graphView.setAutoScrollZone(-1)關閉默認拖拽圖元到邊緣時的自動滾動功能,graphView.handleScroll = function(){};關閉拓撲的滾輪縮放功能,graphView.handlePinch = function(){};關閉拓撲在touch觸屏的縮放功能,由於這些功能都要傳遞給Map交給地圖控制。dom

graphView.mi監聽了moveEnd事件,這個用於某些狀況下,用戶須要手工移動節點,意味着要改變節點的經緯度信息,所以在監聽到moveEnd以後,咱們經過data.lonLat = map.pixelToPoint(new BMap.Pixel(x,y));根據屏幕座標從新獲得移動後的經緯度信息。函數

畢竟GraphView和Map的交互方式是徹底不同的,所以handleClick在用戶點擊界面時咱們須要控制分流,也就是決定這次點擊要交給GraphView處理,仍是Map處理,咱們經過var data = graphView.getDataAt(e);判斷當前是否點擊中了圖元,若是選中了圖元則GraphView拓撲圖接管交互任務,所以經過e.stopPropagation();中止事件的繼續傳播,若是點擊在背景上則無需特殊處理,Map自動會接管任務,畢竟咱們是經過mapDiv.firstChild.firstChild.appendChild(view);的方式插入拓撲圖,mapDiv做爲底層父輩類組件依然會監聽到事件,這點比OpenLayers的整合容易不少,沒有太多須要特殊設置的地方。ui

構建城市節點依然採用了http://llllll.li/randomColor/隨機生成顏色的框架,僅對省會節點增長了漸進色的效果,省會節點和城市節點間構建了ht.Edge連線,經過'flow': true的style啓動連線流動,flow.*還有不少控制參數,例如控制流動的方向,流動的效果等,這裏就再也不展開介紹了。

如下爲操做視頻、抓圖和源代碼供你們參考,這樣的結合可完美的將百度地圖豐富的地圖數據信息,與HT強大的邏輯拓撲功能相結合,不然光靠百度地圖SDK的API的擴展方式,用戶只能作些簡單的效果,例如連線流動等效果都遠不如HT這樣一行代碼就搞定來的容易。http://v.youku.com/v_show/id_XODQxMDcwNjQ0.html


Screen Shot 2014-12-04 at 12.31.13 AM

 

var dataModel = new ht.DataModel();
var graphView = new ht.graph.GraphView(dataModel);              
var map = null;   

function init() {
    map = new BMap.Map("map");   
    var point = new BMap.Point(116.404, 39.915);                          
    map.addTileLayer(new BMap.TrafficLayer());                    
    map.centerAndZoom(point, 4);                    
    map.enableScrollWheelZoom();                       
    map.addControl(new BMap.NavigationControl());                               

    map.setMapStyle({
        features: ["road", "building","water","land"],
        style:'midnight'
    });

    var view = graphView.getView();
    view.className = 'graphView'; 
    var mapDiv = document.getElementById('map');
    mapDiv.firstChild.firstChild.appendChild(view);

    map.addEventListener('movestart', function(e){                   
       view.style.opacity = 0;
    });
    map.addEventListener('moveend', function(e){
       view.style.opacity = 1; 
       resetPosition(); 
    });
    map.addEventListener('dragstart', function(e){
       view.style.opacity = 0;                
    });
    map.addEventListener('dragend', function(e){
       view.style.opacity = 1; 
       resetPosition(); 
    });                                
    map.addEventListener('zoomstart', function(e){
        view.style.opacity = 0;
    });                
    map.addEventListener('zoomend', function(e){
        view.style.opacity = 1;    
        resetPosition(); 
    });                

    graphView.setLayers(['edgeLayer', 'nodeLayer']);
    graphView.enableFlow(60);
    graphView.enableToolTip();
    graphView.getToolTip = function(event){
        var data = this.getDataAt(event);
        if(data instanceof ht.Node){
            return '城市:' + data.s('note') + '
經度:' + data.lonLat.lng + '
維度:' + data.lonLat.lat;
        }
        return null;
    };
    graphView.isVisible = function(data){
        return map.getZoom() > 1 || this.isSelected(data);
    };
    graphView.isNoteVisible = function(data){
        return map.getZoom() > 8 || this.isSelected(data);
    }; 
    graphView.getLabel = function(data){
        if(data instanceof ht.Node){
            return '經度:' + data.lonLat.lng + '\n維度:' + data.lonLat.lat;
        }                    
    };
    graphView.isLabelVisible = function(data){
        return map.getZoom() > 9 || this.isSelected(data);
    }; 

    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.pixelToPoint(new BMap.Pixel(x,y)); 
                }                            
            });
        }
    });

    graphView.setAutoScrollZone(-1);
    graphView.handleScroll = function(){};
    graphView.handlePinch = function(){};

    var handleClick = function(e){
        var data = graphView.getDataAt(e);
        if(data){
            e.stopPropagation();
        }
        else if(e.metaKey || e.ctrlKey){
            e.stopPropagation();
        }
    };
    graphView.getView().addEventListener('click', handleClick, false);                
    graphView.getView().addEventListener('dblclick', handleClick, false);
    graphView.getView().addEventListener(ht.Default.isTouchable ? 'touchstart' : 'mousedown', handleClick, false);

    window.addEventListener("resize", function(e) {
        graphView.invalidate();
    }, false);                 

    var color = randomColor(),
        province = null;
    lines = china.split('\n');
    for(var i=0; i<lines.length; i++) {
        line = lines[i].trim();
        if(line.indexOf('【') === 0){               
            color = randomColor();
            province = null;
        }else{
            var ss = line.split(' ');
            if(ss.length === 3){
                var node = createNode(parseFloat(ss[1].substr(3)), parseFloat(ss[2].substr(3)), ss[0].substr(3), color); 
                if(province){
                    var edge = new ht.Edge(province, node);
                    edge.s({
                        'edge.center': true,
                        'edge.width': 1,                                    
                        'flow': true
                    });
                    edge.setLayer('edgeLayer');
                    dataModel.add(edge);
                }else{
                    province = node;
                    node.s({                                   
                        'shape.gradient': 'radial.center',                                    
                        'border.type': 'circle',
                        'border.width': 1,
                        'border.color': 'rgba(200, 200, 200, 0.5)'                                    
                    });
                    node.setSize(15, 15);
                }
            }
        }
    }                

}

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.setLayer('nodeLayer');
    node.setSize(10, 10);
    var lonLat = new BMap.Point(lon, lat); 
    node.setPosition(map.pointToPixel(lonLat));
    node.lonLat = lonLat;
    graphView.dm().add(node);
    return node;
}            

function resetPosition(e){
    graphView.tx(0);
    graphView.ty(0);
    dataModel.each(function(data){
        if(data instanceof ht.Node){
            var lonLat = data.lonLat,
                position = map.pointToPixel(lonLat);   
            data.setPosition(position.x,position.y);                           
        }
    });            
}
相關文章
相關標籤/搜索