大地算法簡介
- 相關(guān)概念
地軸:即為地球斜軸愧杯,又稱地球自轉(zhuǎn)軸。是指地球自轉(zhuǎn)所繞的軸,北端與地表的交點是北極岔绸,南端與地表的交點是南極
赤道:地球表面的點隨地球自轉(zhuǎn)產(chǎn)生的軌跡中周長最長的圓周線尿背,赤道半徑6,378.137km端仰。
緯度:緯度是指某點與地球球心的連線和地球赤道面所成的線面角,其數(shù)值在0至90度之間田藐。位于赤道以北的點的緯度叫北緯荔烧,記為N,位于赤道以南的點的緯度稱南緯坞淮,記為S茴晋。
經(jīng)度:一般指球面坐標系的縱坐標,具體來說就是地球上一個地點離一根被稱為本初子午線的南北方向走線以東或以西的度數(shù)回窘。
本初子午線:即0度經(jīng)線诺擅,亦稱格林威治子午線或格林尼治子午線,是位于英國格林尼治天文臺的一條經(jīng)線(亦稱子午線)啡直。本初子午線的東西兩邊分別定為東經(jīng)和西經(jīng)烁涌,于180度相遇。
大地算法:
在實際應(yīng)用中酒觅,我們計算2個位置的距離撮执,通常做法就是獲取2個位置中心處的經(jīng)緯度,然后根據(jù)經(jīng)緯度計算它們之間地表弧線距離舷丹。計算地表弧線距離的方法即稱為大地算法抒钱。算法推導(dǎo)
如圖所示,我們計算A點和B點之間的弧線距離颜凯,因此我們的目標要獲取OA谋币、OB之間的夾角,記為C症概。
設(shè)第一點A的經(jīng) 緯度為(jA, wA)蕾额,第二點B的經(jīng)緯度為(jB, wB),按照0度經(jīng)線的基準彼城,東經(jīng)取經(jīng)度的正值(Longitude)诅蝶,西經(jīng)取經(jīng)度負值(-Longitude)退个,北緯取90-緯度值(90- Latitude),南緯取90+緯度值(90+Latitude)调炬,則經(jīng)過上述處理過后的兩點被計為(MLonA, MLatA)和(MLonB, MLatB)语盈。那么根據(jù)三角推導(dǎo),可以得到計算兩點距離的如下公式:
C = sin(MLatA)*sin(MLatB)*cos(MLonA-MLonB) + cos(MLatA)*cos(MLatB)
Distance = R*Arccos(C)*Pi/180
如果僅對經(jīng)度作正負的處理筐眷,而不對緯度作90-Latitude(假設(shè)都是北半球黎烈,南半球只有澳洲具有應(yīng)用意義)的處理,那么公式將是:
C = sin(wA)*sin(wB) + cos(wA)*cos(wB)*cos(jA-jB)
Distance = R*Arccos(C)*Pi/180
針對這種情況匀谣,可以簡單演示下推導(dǎo)過程:
- Java實現(xiàn)
public static double Distance(Location loc1, Location loc2) {
double a, b, R;
R = 6378137; // 地球半徑照棋,單位:米
double lat1 = loc1.latitude;
double long1 = loc1.longitude;
double lat2 = loc2.latitude;
double long2 = loc2.longitude;
a = (lat1 - lat2) * Math.PI / 180.0;
b = (long1 - long2) * Math.PI / 180.0;
double d;
double sa2, sb2;
sa2 = Math.sin(a / 2.0);
sb2 = Math.sin(b / 2.0);
d = 2 * R * Math.asin(Math.sqrt(sa2 * sa2 + Math.cos(lat1) * Math.cos(lat2) * sb2 * sb2));
return d;
}
Spout/Bolt編程
我們的目標是本地不斷的隨機生成一個坐標點,然后計算這個點到一個固定位置的距離武翎。
- Location類
首先烈炭,為了書寫方便,我們先創(chuàng)建一個Location的Class宝恶。
public class Location {
public double longitude;
public double latitude;
public Location(double lon, double lat) {
this.longitude = lon;
this.latitude = lat;
}
public String locationInfo() {
String info = "location:( " + longitude + "," + latitude + " ) ";
return info;
}
}
- RandomLocationSpout類
創(chuàng)建RandomLocationSpout類符隙,繼承BaseRichSpout,并重寫基類的基本方法垫毙。
public class RandomLocationSpout extends BaseRichSpout {
SpoutOutputCollector spoutOutputCollector;
@Override
public void open(Map conf, TopologyContext context, SpoutOutputCollector collector) {
// TODO Auto-generated method stub
spoutOutputCollector = collector;
}
@Override
public void nextTuple() {
// TODO Auto-generated method stub
double lat = 39 + (Math.random()*2);
double lon = 116 + Math.random();
String loc = lon + "," + lat;
spoutOutputCollector.emit(new Values(loc));
}
@Override
public void declareOutputFields(OutputFieldsDeclarer declarer) {
// TODO Auto-generated method stub
declarer.declare(new Fields("spout"));
}
}
在nextTuple()
方法中霹疫,我們隨機生成一個緯度在北緯39度-41度之間,經(jīng)度在東京116度-117度之間的一個坐標综芥,然后將該坐標發(fā)射出去丽蝎。
- CalculateDistantBolt類
創(chuàng)建CalculateDistantBolt類,并重寫IRichBolt接口的相關(guān)方法
public class CalculateDistantBolt implements IRichBolt {
private OutputCollector outputCollector;
public void prepare(Map stormConf, TopologyContext context, OutputCollector collector) {
// TODO Auto-generated method stub
outputCollector = collector;
}
public void execute(Tuple input) {
// TODO Auto-generated method stub
String loc = input.getString(0);
String[] s1 = loc.split(",");
Location location = new Location(Double.parseDouble(s1[0]), Double.parseDouble(s1[1]));
Location center = new Location(116.360664, 40.007614);
double d = Distance(location, center);
System.out.println("************\\n" +location.locationInfo() + "between" + center.locationInfo() + ":\\n" + "Distant: " + d +"\\n ***********");
}
public void cleanup() {
// TODO Auto-generated method stub
}
public void declareOutputFields(OutputFieldsDeclarer declarer) {
// TODO Auto-generated method stub
}
public Map<String, Object> getComponentConfiguration() {
// TODO Auto-generated method stub
return null;
}
public static double Distance(Location loc1, Location loc2) {
double a, b, R;
R = 6378137; // 地球半徑
double lat1 = loc1.latitude;
double long1 = loc1.longitude;
double lat2 = loc2.latitude;
double long2 = loc2.longitude;
a = (lat1 - lat2) * Math.PI / 180.0;
b = (long1 - long2) * Math.PI / 180.0;
double d;
double sa2, sb2;
sa2 = Math.sin(a / 2.0);
sb2 = Math.sin(b / 2.0);
d = 2 * R * Math.asin(Math.sqrt(sa2 * sa2 + Math.cos(lat1) * Math.cos(lat2) * sb2 * sb2));
return d;
}
}
在execute(Tuple input)
方法中膀藐,我們獲取Spout發(fā)射的坐標點屠阻,并計算該點到當前位置的地表距離(實例中center是我當前的位置)。** 打印輸出計算結(jié)果**额各。
- DistantTopology類
最后是創(chuàng)建啟動主類DistantTopology国觉,進行拓撲構(gòu)建。在main
方法中設(shè)置好Spout和Bolt虾啦,然后Topology任務(wù)提交到Storm上麻诀。
public class DistantTopology {
private static TopologyBuilder builder = new TopologyBuilder();
public static void main(String[] args) {
// TODO Auto-generated method stub
Config config = new Config();
builder.setSpout("RandomLocationSpout", new RandomLocationSpout(), 2);
builder.setBolt("CalculateDistantBolt", new CalculateDistantBolt(), 2).shuffleGrouping(
"RandomLocationSpout");
config.setDebug(true);
//通過是否有參數(shù)來控制是否啟動集群,或者本地模式執(zhí)行
if (args != null && args.length > 0) {
try {
config.setNumWorkers(1);
StormSubmitter.submitTopology(args[0], config,
builder.createTopology());
} catch (Exception e) {
e.printStackTrace();
}
} else {
config.setMaxTaskParallelism(1);
LocalCluster cluster = new LocalCluster();
cluster.submitTopology("wordcount", config, builder.createTopology());
}
}
}
提交Topology任務(wù)
- 使用mvn命令將工程打成jar包
- 上傳jar包到集群的主機上
- 主機上傲醉,終端執(zhí)行storm jar命令针饥,提交Topology任務(wù)
- 提交成功后,在對應(yīng)節(jié)點上查看worker日志
1041937 [Thread-8-RandomLocationSpout] INFO backtype.storm.daemon.task - Emitting: RandomLocationSpout default [116.61777982507657,39.25343303306309]
1041937 [Thread-14-CalculateDistantBolt] INFO backtype.storm.daemon.executor - Processing received message source: RandomLocationSpout:2, stream: default, id: {}, [116.72828785729372,39.204690956457334]
************
location:( 116.72828785729372,39.204690956457334 ) betweenlocation:( 116.360664,40.007614 ) :
Distant: 88969.36622189148
***********
1041937 [Thread-8-RandomLocationSpout] INFO backtype.storm.daemon.task - Emitting: RandomLocationSpout default [116.03394550808655,40.8784387953902]
1041937 [Thread-14-CalculateDistantBolt] INFO backtype.storm.daemon.executor - Processing received message source: RandomLocationSpout:2, stream: default, id: {}, [116.87086610893954,40.505468257874966]
************
location:( 116.87086610893954,40.505468257874966 ) betweenlocation:( 116.360664,40.007614 ) :
Distant: 71556.36956545932
***********
日志中會不斷刷新計算距離需频,任務(wù)提交成功。