HT for Web作為邏輯拓撲圖形組件自身沒(méi)有GIS功能,但可以與各種GIS引擎即其客戶(hù)端組件進(jìn)行融合,各取所長(cháng)實(shí)現邏輯拓撲和物理拓撲的無(wú)縫融合,本章將具體介紹HT for Web與開(kāi)發(fā)免費的OpenLayers地圖結合應用的關(guān)鍵技術(shù)點(diǎn),該文介紹的結合的原理,其實(shí)還可推廣到與ArcGIS、百度地圖以及GoogleMap等眾多GIS地圖引擎融合的解決方案。
以上抓圖為本文介紹的例子最終運行效果,接下來(lái)我們一步步來(lái)實(shí)現,首選顯示地圖信息需要有城市經(jīng)緯度數據,搜索了下感謝此篇博客提供的數據。這么大量的數據我采用的是《HT圖形組件設計之道(四)》中介紹的getRawText函數方式,有了數據之后剩下就是呈現的問(wèn)題了,我們需要將HT的GraphView組件與OpenLayers的map地圖組件疊加在一起,也就是OpenLayers的tile地圖圖片在下方,GraphView的組件在上方,由于GraphView默認是透明的,因此非圖元部分用戶(hù)可穿透看到地圖內容。找到合適的組件插入位置是頭疼的事情,ArcGIS、百度地圖包括GoogleMap幾乎每個(gè)不同的GIS組件都需要嘗試一番才能找到合適的插入位置,其他GIS引擎組件的整合以后章節再介紹,本文我們關(guān)注的OpenLayers的插入方式為map.viewPortDiv.appendChild(graphView.getView())。
HT和OpenLayers組件疊加在一起之后,剩下就是拓撲里面圖元的擺放位置與經(jīng)緯度結合的問(wèn)題,常規網(wǎng)絡(luò )拓撲圖中存儲在ht.Node圖元的position是邏輯位置,和經(jīng)緯度沒(méi)有任何關(guān)系,因此在GIS應用中我們需要根據圖元的經(jīng)緯度信息換算出position的屏幕邏輯坐標信息,如果你知道投影算法也可以自己提供函數處理,但所有GIS組件都提供了類(lèi)似的API函數供調用,當然這部分也沒(méi)有標準化,不同的GIS組件需要調用的API都有差異,但基本原理是一致的,對于OpenLayers我們通過(guò)map.getPixelFromLonLat(data.lonLat)可以將經(jīng)緯度信息轉換成屏幕像素邏輯坐標,也就是ht.Node需要的position坐標信息。
細心的同學(xué)會(huì )想到轉換是雙向的,有可能用戶(hù)需要拖動(dòng)圖元節點(diǎn)改變其經(jīng)緯度信息,這時(shí)候我們就需要另外一個(gè)方向函數,即根據屏幕邏輯坐標轉換成當前坐標對應的經(jīng)緯度,在OpenLayers中我們通過(guò)map.getLonLatFromPixel(new OpenLayers.Pixel(x, y));可以搞定。
顯示搞定后剩下就是交互的問(wèn)題了,HT自己有套交互體系,OpenLayers也需要地圖漫游和縮放的交互,兩者如何結合呢?如果能保留住兩者的功能那就最好了,答案時(shí)肯定的,我們只需要添加mousedown或touchstart事件監聽(tīng),如果graphView.getDataAt(e)選中了圖元我們就通過(guò)e.stopPropagation();停止事件的傳播,這樣map地圖就不會(huì )響應,這時(shí)候HT接管了交互,如果沒(méi)有選中圖元則map接管地圖操作的交互。
以上交互設計似乎很完美了,結果運行時(shí)發(fā)現了幾處折騰了我很久才找到解決方案的坑:
設置map.events.fallThrough = true;否則map不會(huì )將事件透傳到HT的GraphView組件
graphView.getView().style.zIndex = 999; 需要指定一定的zIndex否則會(huì )被遮擋
graphView.getView().className = ‘olScrollable’; 否則滾輪不會(huì )響應地圖縮放
設置ht.Default.baseZIndex: 1000 否則ToolTip會(huì )被遮擋
為了讓這個(gè)例子用戶(hù)體驗更友好,我還用心折騰了些技術(shù)點(diǎn)供參考:
采用開(kāi)源免費的http://llllll.li/randomColor/隨機顏色類(lèi)庫,該類(lèi)庫還有很多非常棒的顏色獲取函數,我只是簡(jiǎn)單的為每個(gè)省份顯示不一樣的顏色
重載了isVisible、isNoteVisible和isLabelVisible僅在縮放達到一定級別才顯示更詳細的內容,否則縮小時(shí)所有城市信息都顯示完全無(wú)法查看,多少也能提高顯示性能
以下為最終效果的抓圖、視頻和源代碼://v.youku.com/v_show/id_XODM5Njk0NTU2.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 | 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') + '經(jīng)度:' + 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 '經(jīng)度:' + 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;} |
聯(lián)系客服