在前面《百度地圖、ECharts整合HT for Web網絡拓撲圖應用》咱們有介紹百度地圖和 HT for Web 的整合,咱們今天來談談 OpenLayers 和 HT for Web 的整合。html
HT for Web 做爲邏輯拓撲圖形組件自身沒有 GIS 功能,但能夠與各類 GIS 引擎即其客戶端組件進行融合,各取所長實現邏輯拓撲和物理拓撲的無縫融合,本章將具體介紹 HT for Web 與開發免費的 OpenLayers 地圖結合應用的關鍵技術點,該文介紹的結合的原理,其實還可推廣到與 ArcGIS、百度地圖以及 GoogleMap 等衆多 GIS 地圖引擎融合的解決方案。node
以上抓圖爲本文介紹的例子最終運行效果,接下來咱們一步步來實現,首選顯示地圖信息須要有城市經緯度數據,搜索了下感謝此篇博客提供的數據。這麼大量的數據我採用的是《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
以上交互設計彷佛很完美了,結果運行時發現了幾處折騰了我好久才找到解決方案的坑:函數
設置map.events.fallThrough = true;不然map不會將事件透傳到HT的GraphView組件性能
graphView.getView().style.zIndex = 999; 須要指定必定的zIndex不然會被遮擋this
graphView.getView().className = ‘olScrollable’; 不然滾輪不會響應地圖縮放spa
設置ht.Default.baseZIndex: 1000 不然ToolTip會被遮擋
爲了讓這個例子用戶體驗更友好,我還用心折騰了些技術點供參考:
採用開源免費的http://llllll.li/randomColor/隨機顏色類庫,該類庫還有不少很是棒的顏色獲取函數,我只是簡單的爲每一個省份顯示不同的顏色
重載了isVisible、isNoteVisible和isLabelVisible僅在縮放達到必定級別才顯示更詳細的內容,不然縮小時全部城市信息都顯示徹底沒法查看,多少也能提升顯示性能
如下爲最終效果的抓圖、視頻和源代碼:http://v.youku.com/v_show/id_...
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; }