百度地圖與HT for Web結合的GIS網路拓撲應用

圖撲軟體發表於2014-12-04

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

Screen Shot 2014-12-03 at 11.43.05 PMScreen Shot 2014-12-03 at 11.18.02 PMScreen Shot 2014-12-03 at 11.16.55 PM

百度地圖有多種客戶端SDK,我們這裡用的自然是JavaScript版的API,百度地圖的2.0版增加了不少新功能,例如可以自定義地圖樣式模板,本例中我們特意設定成style:’midnight’的深色背景風格。插入map的位置與OpenLayers也不一樣,通過mapDiv.firstChild.firstChild.appendChild(view);插入,zIndex這些屬性都還好不需要設定。

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

地圖資料方面每個省的省會城市都是第一個出現,因此我們構建了size大一點的帶漸進色的圖元代表省會城市,其他城市構建時同時構建ht.Edge的連線與省會節點連線,同時引入HTht-flow.js外掛,只需要graphView.enableFlow(60);一句話就可以啟動流動連線功能,每條edge連線還可以有很多flow.*相關的流動參考可控制流動效果,這裡的簡單例子我們不需要做定製,保持HT預設的流動引數就足夠酷了。

另外通過graphView.setLayers(['edgeLayer', 'nodeLayer']);我們為拓撲設定了兩個層,node.setLayer(‘nodeLayer’);和edge.setLayer(‘edgeLayer’)使得圖元節點全部呈現於連線之上,從這個細節大家可以發現,以前需要在GIS的SDK擴充套件的功能也可以通過在HTGraphView實現,地圖僅僅是作為背景,業務邏輯程式碼完全在更靈活強大的HT模型中進行。

其他方面細節還由監聽map的縮放等操作對拓撲圖元重新定位,以及監聽互動決定GraphView還是Map進行控制等都與以前的文章類似,這裡就不再介紹了,以下為操作視訊、抓圖和原始碼供大家參考,這樣的結合可完美的將百度地圖豐富的地圖資料資訊,與HT強大的邏輯拓撲功能相結合,否則光靠百度地圖SDK的API的擴充套件方式,使用者只能做些簡單的效果,例如連線流動等效果都遠不如HT這樣一行程式碼就搞定來的容易。http://v.youku.com/v_show/id_XODQxMDcwNjQ0.html

<iframe src="http://player.youku.com/embed/XODQxMDcwNjQ0" frameborder="0" width="510" height="498"></iframe>

Screen Shot 2014-12-03 at 11.12.43 PM

Screen Shot 2014-12-03 at 9.43.40 PMScreen 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);                           
		}
	});            
}

 

相關文章