前言 一个成熟的大型网站(如淘宝、京东等)的系统架构并不是开始设计就具备完整的高性能、高可用、安全等特性,它总是随着用户量的增加,业务功能的扩展 逐渐演变完善的,在这个过程中,开发模式、技术架构、设计思想也发生了很大的变化,就连技术人员也从几个人发展到一个部门甚至一条产品线。所以成熟的系统 架构是随业务扩展而完善出来的,并不是一蹴而就;不同业务特征的系统,会有各自的侧重点,例如淘宝,要解决海量的商品信息的搜索、下单、支付,例如腾讯, 要解决数亿的用户实时消息传输,百度它要处理海量的搜索请求,他们都有各自的业务特性,系统架构也有所不同。尽管如此我们也可以从这些不同的网站背景下, 找出其中共用的技术,这些技术和手段可以广泛运行在大型网站系统的架构中,下面就通过介绍大型网站系统的演化过程,来认识这些技术和手段。 一、最开始的网站架构 最初的架构,应用程序、数据库、文件都部署在一台服务器上,如图:…
April 26, 2015
利用行动装置GPS定位寻找临近地点
早先已展示过,在网页内嵌Google地图、将地址转换为经纬度座标、在地图上显示自订地标图示等技巧,最后来个综合应用当作期末考。本次的练习题是”依使用者所在位置,找出距离最近的五个台北市消防分队”。
简单整理值得留意的技术细节:
- 在网页嵌入Google地图并放上自订标示点(Marker)的做法,可参考笔记-网页内嵌Google地图与地理位置模拟一文。
- 由市政府网站取得台北市消防分队地址,透过地理编码算出经纬度座标,可参考Google Maps API地址转换一文。而在本次范例中,我们预先将查到的经纬度数字一并写入CSV档中,不必每次重新查询。
- 要计算两个经纬度座标间的直线距离,Haversine公式是最常用的演算法,简单来说,就是把地球当成一个圆球,用球体表面任两点到圆心所形成的夹角,加上一堆Sin , Cos推算沿球体表面连接两点的弧线长度。依据英国学者研究指出,思考过度复杂数学公式可能会对中老年人的神经中枢有负面影响,为求养生保健,在此直接引用公式,对于数学细节就不再深究…
- HTML5世代的浏览器(IE要IE9+)多能支援地理资讯功能,可整合行动装置(手机、平版)的GPS取得使用者当时所在地理位置(存取前会弹出确认视窗征求使用者同意),如此我们便可依使用者所在位置提供不同资讯,例如:列出临近的商店、餐厅或服务据点…等等。要透过Javascript存取使用者的地理资讯,可使用Geolocation API。
- 使用者所在位置及各消防分队的经纬度都有了,便可利用Haversine公式算出各分队与目前位置的直线距离作为远近参考。(不考虑路线规划、交通状况等因素,那是导航软体或霹雳车RD该烦恼的事,为了好玩写程式没必要把自己逼上绝路XD至于有心挑战的朋友,Google Maps也有路线规划API,倒是可以参考)
- Javascript的Array.sort(compareFunction),提供了类似LINQ OrderBy(o => o.prop)的简便做法,让我惊喜了一下。原本以为要花点心思处理,没想到只用两行就搞定依距离远近排序的需求。
- 前篇文章介绍的动态文字图档权充Marker Icon派上用场,直接用分队名称告示牌当标示图,最近的前五名用鲜红底,其余用暗红底,一目了然,酷!!
程式范例如下,请享用:
<!DOCTYPE html> <html> <head runat="server"> <title>Geocoding Test</title> <script type='text/javascript' src='http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.7.1.js'></script> <script src="https://maps.google.com/maps/api/js?sensor=true"></script> <script src="DynaMarkerIcon.js" type="text/javascript"></script> <meta name="viewport" content="initial-scale=1.0, user-scalable=no" /> <style> body,input { font-size: 9pt; } html { height: 100% } body { height: 100%; margin: 0px; padding: 0px } #map_canvas { height: 100% } </style> <script> $(function () { //計算兩個經緯座標間的距離(Haversine公式) function distHaversine(p1, p2) { var rad = function (x) { return x * Math.PI / 180; } var R = 6371; // earth's mean radius in km var dLat = rad(p2.lat() - p1.lat()); var dLong = rad(p2.lng() - p1.lng()); var a = Math.sin(dLat / 2) * Math.sin(dLat / 2) + Math.cos(rad(p1.lat())) * Math.cos(rad(p2.lat())) * Math.sin(dLong / 2) * Math.sin(dLong / 2); var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); var d = R * c; return parseFloat(d.toFixed(3)); } //消防分局資料陣列 var branches = []; //取得分局資料(含經緯座標),存為物作陣列 $.get("AddrList.txt", {}, function (list) { var lines = list.replace(/r/g, "").split('n'); //lines[i]格式如下: //文山中隊-木柵分隊,(02)29391604,文山區木柵路2段200號,24.9890353,121.5630845 for (var i = 0; i < lines.length; i++) { var parts = lines[i].split(','); branches.push({ name: parts[0], tel: parts[1], addr: parts[2], latlng: new google.maps.LatLng( parseFloat(parts[3]), parseFloat(parts[4])), dist: 0 }); } getGeolocation(); }); //取得使用者目前位罝 function getGeolocation() { if (navigator && navigator.geolocation) { //getCurrentPosition屬非同步執行,要另定函數解析結果 navigator.geolocation.getCurrentPosition(parsePosition); } } //解析getCurrentPosition傳回的結果 function parsePosition(pos) { //標示點陣列 var markers = []; //由pos.coords取出latitude及longitude var curLatLng = new google.maps.LatLng( pos.coords.latitude, pos.coords.longitude); //分別計算對所有Branch的距離 for (var i = 0; i < branches.length; i++) { var branch = branches[i]; branch.distance = //計算兩個LatLng間的距離 distHaversine(branch.latlng, curLatLng); } //依距離進行排序 branches.sort(function (a, b) { if (a.distance == b.distance) return 0; return (a.distance > b.distance) ? 1 : -1; }); //設定地圖參數 var mapOptions = { center: curLatLng, mapTypeId: google.maps.MapTypeId.ROADMAP //正常2D道路模式 }; //在指定DOM元素中嵌入地圖 var map = new google.maps.Map( document.getElementById("map_canvas"), mapOptions); //使用LatLngBounds統計檢視範圍 var bounds = new google.maps.LatLngBounds(); //加入使用者所在位置 var marker = new google.maps.Marker({ position: curLatLng, title: "現在位置", //借用前篇文章介紹的Canvas.toDataURL()產生動態圖檔作為圖示 icon: createMarkerIcon("現在位置"), map: map }); var h = []; //因為已排序過,故會依距離由近到遠加入Marker for (var i = 0; i < branches.length; i++) { var b = branches[i]; //距離最近的前五名加入檢視範圍 if (i < 5) bounds.extend(b.latlng); var marker = new google.maps.Marker({ position: b.latlng, title: b.name, //以刻有分隊名稱的告示牌作為圖示 icon: createMarkerIcon(b.name.split('-')[1], //距離較近的前五名為紅底,其餘為暗紅底 { bgColor: i < 5 ? 'red' : 'darkred' }), map: map }); } //調整檢視範圍 map.fitBounds(bounds); } }); </script> </head> <body> <div id="map_canvas" style="width:100%;%20height:100%"></div> </body> </html>
执行结果如下:
原文:http://blog.darkthread.net/post-2012-06-19-find-nearest-geolocation-markers.aspx