Source: src/h5.api.geo.js

  1. /*
  2. * Copyright (C) 2012-2016 NS Solutions Corporation
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. *
  16. * hifive
  17. */
  18. /* ------ h5.api.geo ------ */
  19. (function() {
  20. // =========================================================================
  21. //
  22. // Constants
  23. //
  24. // =========================================================================
  25. // =============================
  26. // Production
  27. // =============================
  28. /** エラコード: 指定された緯度または経度の値が不正 */
  29. var ERR_CODE_INVALID_COORDS = 2000;
  30. /** エラーコード: getDistance()で、指定された計算モードの定数が不正 */
  31. var ERR_CODE_INVALID_GEOSYSTEM_CONSTANT = 2001;
  32. /** エラーコード: 位置情報の取得に失敗 */
  33. var ERR_CODE_POSITIONING_FAILURE = 2002;
  34. // =============================
  35. // Development Only
  36. // =============================
  37. /* del begin */
  38. var errMsgMap = {};
  39. errMsgMap[ERR_CODE_INVALID_COORDS] = '正しい緯度または経度を指定して下さい。';
  40. errMsgMap[ERR_CODE_INVALID_GEOSYSTEM_CONSTANT] = '正しい計算モード定数を指定して下さい';
  41. errMsgMap[ERR_CODE_POSITIONING_FAILURE] = '位置情報の取得に失敗しました。';
  42. addFwErrorCodeMap(errMsgMap);
  43. /* del end */
  44. // =========================================================================
  45. //
  46. // Cache
  47. //
  48. // =========================================================================
  49. // navigator.geolocationをキャッシュする変数
  50. var geo = null;
  51. function getGeo() {
  52. if (!geo) {
  53. geo = navigator.geolocation;
  54. }
  55. return geo;
  56. }
  57. var h5ua = h5.env.ua;
  58. // =========================================================================
  59. //
  60. // Privates
  61. //
  62. // =========================================================================
  63. // =============================
  64. // Variables
  65. // =============================
  66. // =============================
  67. // Functions
  68. // =============================
  69. // =========================================================================
  70. //
  71. // Body
  72. //
  73. // =========================================================================
  74. /**
  75. * h5.api.geo.getDistance() の計算モードを指定するための定数クラス
  76. * <p>
  77. * このオブジェクトは自分でnewすることはありません。以下のオブジェクトにアクセスするとインスタンスが返されます。
  78. * </p>
  79. * <ul>
  80. * <li>h5.api.geo.GS_GRS80</li>
  81. * <li>h5.api.geo.GS_BESSEL</li>
  82. * </ul>
  83. *
  84. * @class
  85. * @name GeodeticSystemEnum
  86. */
  87. function GeodeticSystemEnum(oblateness, semiMajorAxis) {
  88. // 扁平率
  89. this.oblateness = oblateness;
  90. // 長(赤道)半径
  91. this.semiMajorAxis = semiMajorAxis;
  92. }
  93. /**
  94. * 扁平率を取得します。
  95. *
  96. * @memberOf GeodeticSystemEnum
  97. * @name getOblateness
  98. * @returns {Number} 扁平率
  99. */
  100. GeodeticSystemEnum.prototype.getOblateness = function() {
  101. return this.oblateness;
  102. };
  103. /**
  104. * 長(赤道)半径を取得します。
  105. *
  106. * @memberOf GeodeticSystemEnum
  107. * @name getSemiMajorAxis
  108. * @returns {Number} 長(赤道)半径
  109. */
  110. GeodeticSystemEnum.prototype.getSemiMajorAxis = function() {
  111. return this.semiMajorAxis;
  112. };
  113. /** 計算モード: 世界測地系(GRS80) */
  114. var GRS80 = new GeodeticSystemEnum(298.257222, 6378137);
  115. /** 計算モード: 日本測地系(BESSEL) */
  116. var BESSEL = new GeodeticSystemEnum(299.152813, 6377397.155);
  117. /** ラジアン毎秒 - 1度毎秒 */
  118. var DEGREES_PER_SECOND = Math.PI / 180;
  119. /**
  120. * Geolocation API
  121. *
  122. * @memberOf h5.api
  123. * @name geo
  124. * @namespace
  125. */
  126. function Geolocation() {
  127. // 空コンストラクタ
  128. }
  129. $.extend(Geolocation.prototype, {
  130. /**
  131. * Geolocation APIが使用可能であるかの判定結果<br>
  132. *
  133. * @type Boolean
  134. * @memberOf h5.api.geo
  135. * @name isSupported
  136. */
  137. // IE9の場合、navigator.geolocationにアクセスするとメモリーリークするのでエージェントで利用可能か判定する
  138. isSupported: (h5ua.isIE && h5ua.browserVersion >= 9) ? true : !!getGeo(),
  139. /**
  140. * 現在地の緯度・経度を取得します。
  141. *
  142. * @memberOf h5.api.geo
  143. * @name getCurrentPosition
  144. * @function
  145. * @param {Object} [option] 設定情報
  146. * @param {Boolean} [option.enableHighAccuracy] 正確な位置を取得するか (ただし消費電力の増加や応答が遅延する)
  147. * @param {Number} [option.timeout] 位置情報を取得するまで待機する時間 (ミリ秒)
  148. * @param {Number} [option.maximumAge] キャッシュされた位置情報の有効期間を指定する (ミリ秒)
  149. * @returns {Promise} Promiseオブジェクト
  150. */
  151. getCurrentPosition: function(option) {
  152. var dfd = h5.async.deferred();
  153. getGeo().getCurrentPosition(function(geoPosition) {
  154. dfd.resolve(geoPosition);
  155. }, function(e) {
  156. dfd.reject(createRejectReason(ERR_CODE_POSITIONING_FAILURE, null, e));
  157. }, option);
  158. return dfd.promise();
  159. },
  160. /**
  161. * 現在地の緯度・経度を定期的に送信します。
  162. * <p>
  163. * このメソッドは定期的に位置情報を取得するため、Deferred.progress()で値を取得します。<br>
  164. * (Deferred.done()では値を取得できません。)
  165. * <p>
  166. * <b>実装例</b><br>
  167. *
  168. * <pre>
  169. * h5.api.geo.watchPosition().progress(function(pos) {
  170. * // 変数 pos に位置情報が格納されている。
  171. * });
  172. * </pre>
  173. *
  174. * @memberOf h5.api.geo
  175. * @name watchPosition
  176. * @function
  177. * @param {Object} [option] 設定情報
  178. * @param {Boolean} [option.enableHighAccuracy] 正確な位置を取得するか (ただし消費電力の増加や応答が遅延する)
  179. * @param {Number} [option.timeout] 位置情報を取得するまで待機する時間 (ミリ秒)
  180. * @param {Number} [option.maximumAge] キャッシュされた位置情報の有効期間を指定する (ミリ秒)
  181. * @returns {WatchPositionPromise} WatchPositionPromiseオブジェクト
  182. */
  183. watchPosition: function(option) {
  184. var dfd = h5.async.deferred();
  185. var id = getGeo().watchPosition(function(pos) {
  186. dfd.notify(pos);
  187. }, function(e) {
  188. getGeo().clearWatch(id);
  189. dfd.reject(createRejectReason(ERR_CODE_POSITIONING_FAILURE, null, e));
  190. }, option);
  191. /**
  192. * h5.api.geo.watchPositionがこのオブジェクトをプロミス化して返します。
  193. * <p>
  194. * このオブジェクトは自分でnewすることはありません。<b>h5.api.geo.watchPosition</b>関数を呼び出すとインスタンスが返されます。
  195. * </p>
  196. *
  197. * @class
  198. * @name WatchPositionPromise
  199. */
  200. function WatchPositionPromise() {
  201. // 空コンストラクタ
  202. }
  203. /**
  204. * h5.api.geo.watchPositionで行っているユーザの位置監視を終了します。
  205. * <p>
  206. * ユーザの位置監視を終了し、Deferred.done()が実行されます。
  207. * </p>
  208. *
  209. * @memberOf WatchPositionPromise
  210. * @name unwatch
  211. */
  212. WatchPositionPromise.prototype.unwatch = function() {
  213. getGeo().clearWatch(id);
  214. dfd.resolve();
  215. };
  216. return dfd.promise(new WatchPositionPromise());
  217. },
  218. /**
  219. * ヒュベニの法則を使用して、2点間の緯度・経度から直線距離(m)を取得します。
  220. * <p>
  221. * 定数に使用している長半径・扁平率は国土地理院で紹介されている値を使用。
  222. * <p>
  223. * 注意:アルゴリズム上、長距離(100km以上)の地点を図る場合1m以上の誤差が出てしまいます。
  224. * <h4>計算モードの指定方法</h4>
  225. * 計算モードの指定は以下の定数クラスを使用します。<br>
  226. * <table border="1">
  227. * <tr>
  228. * <td>h5.api.geo.GS_GRS80</td>
  229. * <td>世界測地系</td>
  230. * </tr>
  231. * <tr>
  232. * <td>h5.api.geo.GS_BESSEL</td>
  233. * <td>日本測地系</td>
  234. * </tr>
  235. * </table>
  236. *
  237. * @memberOf h5.api.geo
  238. * @name getDistance
  239. * @function
  240. * @param {Number} lat1 地点1の緯度
  241. * @param {Number} lng1 地点1の経度
  242. * @param {Number} lat2 地点2の緯度
  243. * @param {Number} lng2 地点2の経度
  244. * @param {GeodeticSystemEnum} [geoSystem] 計算モード定数
  245. * (h5.api.geo.GS_GRS80:世界測地系(未指定の場合このモードで計算する) / h5.api.geo.GS_BESSEL: 日本測地系)
  246. * @returns {Number} 2点間の直線距離
  247. */
  248. // TODO 長距離の場合も考えて、距離によって誤差が大きくならない『測地線航海算法』で計算するメソッドの追加も要検討
  249. getDistance: function(lat1, lng1, lat2, lng2, geoSystem) {
  250. if (!isFinite(lat1) || !isFinite(lng1) || !isFinite(lat2) || !isFinite(lng2)) {
  251. throwFwError(ERR_CODE_INVALID_COORDS);
  252. }
  253. var geodeticMode = geoSystem ? geoSystem : GRS80;
  254. if (!(geodeticMode instanceof GeodeticSystemEnum)) {
  255. throwFwError(ERR_CODE_INVALID_GEOSYSTEM_CONSTANT);
  256. }
  257. // 長半径(赤道半径)
  258. var A = geodeticMode.getSemiMajorAxis();
  259. // 扁平率
  260. var O = geodeticMode.getOblateness();
  261. // 起点の緯度のラジアン
  262. var latRad1 = lat1 * DEGREES_PER_SECOND;
  263. // 起点の経度のラジアン
  264. var lngRad1 = lng1 * DEGREES_PER_SECOND;
  265. // 終点の緯度のラジアン
  266. var latRad2 = lat2 * DEGREES_PER_SECOND;
  267. // 終点の経度のラジアン
  268. var lngRad2 = lng2 * DEGREES_PER_SECOND;
  269. // 2点の平均緯度
  270. var avgLat = (latRad1 + latRad2) / 2;
  271. // 第一離心率
  272. var e = (Math.sqrt(2 * O - 1)) / O;
  273. var e2 = Math.pow(e, 2);
  274. var W = Math.sqrt(1 - e2 * Math.pow(Math.sin(avgLat), 2));
  275. // 短半径(極半径)
  276. var semiminorAxis = A * (1 - e2);
  277. // 子午線曲率半径
  278. var M = semiminorAxis / Math.pow(W, 3);
  279. // 卯酉船曲率半径
  280. var N = A / W;
  281. // 2点の緯度差
  282. var deltaLat = latRad1 - latRad2;
  283. // 2点の経度差
  284. var deltaLon = lngRad1 - lngRad2;
  285. return Math.sqrt(Math.pow(M * deltaLat, 2)
  286. + Math.pow(N * Math.cos(avgLat) * deltaLon, 2));
  287. },
  288. /**
  289. * getDistanceメソッドで使用する計算モード定数 (世界測地系:GRS80)
  290. *
  291. * @constant
  292. * @memberOf h5.api.geo
  293. * @name GS_GRS80
  294. */
  295. GS_GRS80: GRS80,
  296. /**
  297. * getDistanceメソッドで使用する計算モード定数 (日本測地系:BESSEL)
  298. *
  299. * @constant
  300. * @memberOf h5.api.geo
  301. * @name GS_BESSEL
  302. */
  303. GS_BESSEL: BESSEL
  304. });
  305. // =============================
  306. // Expose to window
  307. // =============================
  308. h5.u.obj.expose('h5.api', {
  309. geo: new Geolocation()
  310. });
  311. })();