根據經緯度繪製座標點相對位置(分別用php和html5實現)

liusaint1992發表於2015-11-05

本文主要講實現 

1.用php實現根據座標點經緯度繪製座標點相對位置。

2.用html5實現根據座標點經緯度繪製座標點相對位置。


之前有一個需求,是要把一些座標點的相對位置畫到一張圖片上,標註出點,點名,並且點之間兩兩連線生成一張圖片。

最開始考慮用html5實現。 但實際上我們生成的圖片要在後臺放到word文件中去,所以最後使用的php在後臺畫圖,然後儲存到本地的方式 。


這幾天正好在研究html5,就用html5的方式再實現了一遍。感覺html5畫起來還要順手一些。 

當然,php也好,html5也好,都只是工具,實現起來主要還是要有演算法。 

php跟javascript使用起來真是很像啊。



需求分析:

1.圖片不能無限大。有一個最大值maxsize。 我們這裡設為300px。也就是生成的圖片,座標區域寬或高最大不超過300px。

2.所有的座標點都要在我們的圖片中。

3.座標點之間的位置關係儘可能真實反映座標情況。

4.座標點。點名。連線顯示在地圖上。


由需求看出難點在第3條上。經緯度座標怎麼換算成我們可以使用來畫圖的座標呢?

經過一番思考,確定了一個粗略的方法:


 大概有這麼幾個步驟:

首先確認一個前提:就是我們假設經度和緯度在地圖上攤開來是1比1的關係,即是說經度1度的距離約等於緯度1度的距離。 當然事實情況並不是這樣,我們這裡只是求一個粗略的位置關係。


1.求出所有座標點的最大經度差,最大緯度差。比較,將大的那一個的長度作為maxsize。

2.根據最大經度差最大維度差之間的比例,可以算出我們短的那一邊的長度。 

3.我們求出一個Rate。這個Rate值表示單位度數在生成圖片上所佔的尺寸,生成一個類似比例尺的東西。

4.然後我們現在已經確定了我們的圖片的長和寬。同時我們以最小經度,最小緯度為圖片的x,y軸原點。然後再根據Rate,以及當前點的經度、緯度與最小經度,最小緯度的差,求出當前點在圖片上繪製時候的座標。

5.求出了繪圖座標之後,就可以使用php。或者html5的方法來進行繪圖操作了。

ps:對於經度差或緯度差為0的情況下我也做了單獨處理。


html5繪製經緯度座標點位置程式碼:

<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8">
	<title>用html5繪製座標點相對位置</title>
</head>
<style>
	canvas{
		border: 1px solid #ccc;
	}
</style>
<script type="text/javascript">

	//繪圖資料準備
	var points=[[],[],[],[]];
	points[0]['L'] = 110.83717794;
	points[0]['B'] = 13;
	points[0]['Name'] = 'pt1';
	points[1]['L'] = 113;
	points[1]['B'] = 24;
	points[1]['Name'] = 'pt2';
	points[2]['L'] = 105.794566000;
	points[2]['B'] = 20;
	points[2]['Name'] = 'pt3';
	points[3]['L'] = 120;
	points[3]['B'] = 17;
	points[3]['Name'] = 'pt4';


	function draw (points) {

	//座標區域長或寬,最大為300px。判斷標準:座標經度差與緯度差,大的那個為300px。再根據經度差與緯度差的比例計算出短的一邊有多少px。
	var MAXSIZE = 300; 
	var maxL = points[0]['L'];
	var maxB=points[0]['B'];
	var minL = points[0]['L'];
	var minB=points[0]['B'];

	var value;

	for(var i=0,pointsLen=points.length;i<pointsLen;i++){
		value = points[i];
		maxL = maxL <value['L']?value['L'] :maxL;
		maxB = maxB <value['B']?value['B'] :maxB;
		minL = minL  > value['L']?value['L'] :minL;
		minB = minB >value['B']?value['B'] :minB;
	}

var diffL = maxL - minL;//經度差
var diffB = maxB - minB;//緯度差

var width,height,Rate,diff;
//計算座標區域height width;

if(diffL == 0){
	width =MAXSIZE;
	height = MAXSIZE;
	Rate = MAXSIZE/parseFloat(diffB);
}
else if (diffB == 0) {
	width =MAXSIZE;
	height = MAXSIZE;
	Rate = MAXSIZE/parseFloat(diffL);
}else if(diffL >= diffB){
	diff = diffL;
	width = MAXSIZE;
	Rate = MAXSIZE/parseFloat(diffL);//單位座標的有多少個px值。
	height = diffB/diffL*MAXSIZE;
}else {
	diff = diffB;
	height = MAXSIZE;
	Rate = MAXSIZE/parseFloat(diffB);//單位座標的有多少個px值。
	width = diffL/diffB*MAXSIZE;
}



var ctx = document.getElementById("myCanvas").getContext("2d");
ctx.translate(20,20);//原點往右下方分別移動20px,多出來的是用來防止名字,以及點上的圓點顯示不下


// 根據B,L計算畫素位置。計算應該有px。
for(var k=0,pointsLen=points.length;k<pointsLen;k++){
	value = points[k];
	if(diffL == 0){
		points[k]['Lpx']  =MAXSIZE/2;
		points[k]['Bpx']  = parseInt(height - (value['B'] - minB)*Rate);
	}
	else if (diffB == 0) {
		points[k]['Lpx'] =parseInt((value['L'] - minL)* Rate);
		points[k]['Bpx'] = MAXSIZE/2;
	} else {
		points[k]['Lpx'] =  parseInt((value['L'] - minL)* Rate);
		points[k]['Bpx'] = parseInt(height - (value['B'] - minB)*Rate);
	}
}

ctx.font = "bold 14px Arial";
ctx.textAlign = "left";
ctx.textBaseline = "middle";

for(var x=0,pointsLen=points.length;x<pointsLen;x++){
	value = points[x];
	i = x +1;
	ctx.strokeStyle='black';
	//兩兩相連的處理
	while (i<pointsLen) {
		ctx.beginPath();
		ctx.moveTo(value['Lpx'] , value['Bpx']);
		ctx.lineTo(points[i]['Lpx'],  points[i]['Bpx']);
		ctx.stroke();
		i++;
	}
	//畫點名
	ctx.beginPath();
	ctx.fillStyle ='black';
	ctx.fillText(value.Name, value['Lpx']+5, value['Bpx']);
	//畫點
	ctx.fillStyle = 'rgb(29,143,254)';
	ctx.beginPath();
	ctx.arc(value['Lpx'] , value['Bpx'] , 5, 0, Math.PI*2, false);
	ctx.fill();

}

}
</script>

<body onload="draw(points)">
	<canvas id="myCanvas" width="350" height="350"></canvas>
</body>
</html>


php繪製經緯度座標點位置程式碼:程式碼看起來要複雜一點,可能是對php畫圖研究的還不夠。。

<?php

//測試資料
$point[0]['L'] = 110.83717794;
$point[0]['B'] = 13;
$point[0]['Name'] = 'pt1';
$point[1]['L'] = 113;
$point[1]['B'] = 24;
$point[1]['Name'] = 'pt2';
$point[2]['L'] = 105.794566000;
$point[2]['B'] = 20;
$point[2]['Name'] = 'pt3';
$point[3]['L'] = 120;
$point[3]['B'] = 17;
$point[3]['Name'] = 'pt4';


function draw($point)
{
//1.計算四個範圍。

$maxL = $point[0]['L'];
$maxB=$point[0]['B'];
$minL = $point[0]['L'];
$minB=$point[0]['B'];

foreach ($point as $key => $value) {
	$maxL = $maxL <$value['L']?$value['L'] :$maxL;
	$maxB = $maxB <$value['B']?$value['B'] :$maxB;
	$minL = $minL  > $value['L']?$value['L'] :$minL;
	$minB = $minB >$value['B']?$value['B'] :$minB;
}

$MAXSIZE = 300; //座標區域長或寬,最大為300px。判斷標準:座標經度差與緯度差,大的那個為300px。再根據經度差與緯度差的比例計算出短的一邊有多少px。

$diffL = $maxL - $minL;//經度差
$diffB = $maxB - $minB;//緯度差

//計算座標區域$height $width;
if($diffL == 0){
	$width =$MAXSIZE;
	$height = $MAXSIZE;
	$Rate = $MAXSIZE/(float)$diffB;
}
elseif ($diffB == 0) {
	$width =$MAXSIZE;
	$height = $MAXSIZE;
	$Rate = $MAXSIZE/(float)$diffL;
}else if($diffL >= $diffB){
	$diff = $diffL;
	$width = $MAXSIZE;
	$Rate = $MAXSIZE/(float)$diffL;//單位座標的有多少個px值。
	$height = $diffB/$diffL*$MAXSIZE;
}else{
	$diff = $diffB;
	$height = $MAXSIZE;
	$Rate = $MAXSIZE/(float)$diffB;//單位座標的有多少個px值。
	$width = $diffL/$diffB*$MAXSIZE;
}

$img_width = $width + 50;//多出來的50是用來防止基站名字,以及點上的圓點顯示不下
$img_height = $height + 30;//多出來的30是用來防止基站名字,以及點上的圓點顯示不下
$image = imagecreatetruecolor($img_width,$img_height);//生成一個黑色背景的圖片。
$back = imagecolorallocate($image, 255, 255, 255);//背景顏色,白色
imagefilledrectangle ($image,0,0,$img_width ,$img_height ,$back);//設定背景,其實是用的白色填充矩形。
$linecolor=  imagecolorallocate($image,0,0,0);//連線的顏色
$pointcolor = imagecolorallocate($image,29,143,254);//點的顏色
$textcolor = imagecolorallocate($image,0,0,0);//字型顏色,黑色

// 根據B,L計算畫素位置。計算應該有px。多出來的6,和10,表示所有座標都向右移動6px,向下移動10px,也是避免基站名和圓點顯示不下。
foreach ($point as $key => $value) {
	if($diffL == 0){
		$point[$key]['Lpx']  =$MAXSIZE/2;
		$point[$key]['Bpx']  = (int)($height - ($value['B'] - $minB)*$Rate)+10;
	}
	elseif ($diffB == 0) {
		$point[$key]['Lpx'] =(int)(($value['L'] - $minL)* $Rate) + 6;
		$point[$key]['Bpx'] = $MAXSIZE/2;
	}else{
		$point[$key]['Lpx'] =  (int)(($value['L'] - $minL)* $Rate) + 6;
		$point[$key]['Bpx'] = (int)($height - ($value['B'] - $minB)*$Rate)+10;
	}
}


$pointNum = count($point);

foreach ($point as $key => $value) {
	$i = $key +1;
	while ($i<$pointNum) {
		imageline($image , $value['Lpx'] , $value['Bpx'], $point[$i]['Lpx'],  $point[$i]['Bpx'] ,  $linecolor );//點與點之間連線
		$i++;
	}
	imagestring( $image ,10 , $value['Lpx'], $value['Bpx'], $value['Name'] ,$linecolor);  //每個點的位置寫下點名。10是大小。
	imagefilledellipse($image , $value['Lpx'] , $value['Bpx'], 10 , 10, $pointcolor); //每個點的位置,用一個填充的圓點表示。
}


// 輸出影象
header("Content-type: image/jpeg");
// imagejpeg($image,'d:\1.jpeg');//如果沒有路徑就輸出圖片,有路徑就儲存圖片。
imagejpeg($image);//如果沒有路徑就輸出圖片,有路徑就儲存圖片。
}


draw($point);

?>


接下來,對比圖!!!

html5繪製畫出來的經緯度座標位置關係圖:


php繪製畫出來的經緯度座標位置關係圖:


再接下來,我們到地圖上看這幾個座標的真實位置:(資料來自之前用天地圖WEB API寫的一個座標展繪的功能,可以把輸入的座標點在地圖上顯示出來)



如果對精確度要求不是特別高的話,我這個方法應該還是可以用的。    有什麼不足的地方,歡迎指出探討。


最後總結一句,其實會某個技能倒是次要的,技能用的時候可以學,更重要的還是要有想法來用這個技能。。。


演示地址:

html5:http://runningls.com/demos/2015/position/html5.html

php:http://runningls.com/demos/2015/position/php.php

github:https://github.com/liusaint/earth/tree/master/position


相關文章