Google Maps APIで円を描く。
- マウスだけでなんとかしたい
- このページの内容はV2版でしたが、動くようにV3版に書き換えてあります。一応V3版は別のページにあります。
google.maps.Circle とか google.maps.Oval...なんてクラスがなかった時代に、google.maps.Polylineで多角形を描くという道をたどった備忘録のページ。
日本へそ公園を中心とする、半径R kmの円で考えてみます。
日本へそ公園を中心とする、半径R kmの円で考えてみます。
各頂点の座標は極座標形式の円の方程式を用いて・・・
経度1度と緯度1度での長さ(r)が異なるから、このままではダメ。
x = R * cos(θ)
y = R * sin(θ)
で、θを0~360まで1周させればいいかと思ったけど、y = R * sin(θ)
経度1度と緯度1度での長さ(r)が異なるから、このままではダメ。
楕円の方程式を持ってきて
理科年表に掲載されている「地球楕円体に関する計算式」を用いて、
緯度をθ、地球の赤道半径をER、地球の離心率をeとすれば、
離心率(e)は扁平率(f)との関係から、
lat = (R / a) * sin(θ) + lat_center
lng = (R / b) * cos(θ) + lng_center
aやbには経度・緯度1度あたりの長さをあてはめればいいわけです。lng = (R / b) * cos(θ) + lng_center
理科年表に掲載されている「地球楕円体に関する計算式」を用いて、
緯度をθ、地球の赤道半径をER、地球の離心率をeとすれば、
経度1度あたりの長さ(m) = (π * ER * (1 - e2)) / (180 * (1 - e2 sin2(θ))3/2)
緯度1度あたりの長さ(m) = (π * ER * cos(θ)) / (180 * (1 - e2 sin2(θ))1/2)
WGS84測地系では、赤道半径は 6378137m。緯度1度あたりの長さ(m) = (π * ER * cos(θ)) / (180 * (1 - e2 sin2(θ))1/2)
離心率(e)は扁平率(f)との関係から、
e2 = 2f - f2
WGS84では 1/f = 298.257223 なので、F = 1/f とすればe2 = 2f - f2 = (2F - 1) / F2
これらを用いて、
// 中心地(日本へそ公園) var point = new google.maps.LatLng(35, 135); // 描く円の半径(km) var radius = 1; // 赤道半径(m) (WGS-84) var EquatorialRadius = 6378137; // 扁平率の逆数 : 1/f (WGS-84) var F = 298.257223; // 離心率の2乗 var E = ((2 * F) -1) / Math.pow(F, 2); // 赤道半径 × π var PI_ER = Math.PI * EquatorialRadius; // 1 - e^2 sin^2 (θ) var TMP = 1 - E * Math.pow(Math.sin(point.lat() * Math.PI / 180), 2); // 経度1度あたりの長さ(m) var arc_lat = (PI_ER * (1 - E)) / (180 * Math.pow(TMP, 3/2)); // 緯度1度あたりの長さ(m) var arc_lng = (PI_ER * Math.cos(point.lat() * Math.PI / 180)) / (180 * Math.pow(TMP, 1/2)); // 半径をm単位に var R = radius * 1000; // 0~360°で1°ずつ頂点を求めることで360角形を生成 var points = new Array(360); for (i = 0; i <= 360; i++) { var rad = i / 180 * Math.PI; var lat = (R / arc_lat) * Math.sin(rad) + point.lat(); var lng = (R / arc_lng) * Math.cos(rad) + point.lng(); points[i] = new google.maps.LatLng(lat, lng); } var circle = new google.maps.Polyline({ path: points, strokeColor: '#ff0000', strokeOpacity: 1.0, strokeWeight: 1, map: map });
ところで、何角形になれば円とみなせますか?
以上を踏まえて、functionにしてみます。
頂点の数はvertexで定義しているので、適宜変更のこと。
頂点の数はvertexで定義しているので、適宜変更のこと。
/** * pseudoGCircle (擬似-円描画) * @param point 中心地 google.maps.LatLng * @param radius 半径 km * @param color google.maps.Polylineに渡される * @param weight google.maps.Polylineに渡される * @param opacity google.maps.Polylineに渡される * @return google.maps.Polyline */ function pseudoGCircle(point, radius, color, weight, opacity) { var vertex = 360; // 頂点の数 var EquatorialRadius = 6378137; // 赤道半径 (WGS-84) var F = 298.257223563; // 扁平率の逆数 (WGS-84) // 離心率の2乗 var E = ((2 * F) -1) / Math.pow(F, 2); // π × 赤道半径 var PI_ER = Math.PI * EquatorialRadius; // 1 - e^2 sin^2 (θ) var TMP = 1 - E * Math.pow(Math.sin(point.lat() * Math.PI / 180), 2); // 経度1度あたりの長さ(m) var arc_lat = (PI_ER * (1 - E)) / (180 * Math.pow(TMP, 3/2)); // 緯度1度あたりの長さ(m) var arc_lng = (PI_ER * Math.cos(point.lat() * Math.PI / 180)) / (180 * Math.pow(TMP, 1/2)); // 半径をm単位に var R = radius * 1000; var points = new Array(vertex); for (i = 0; i <= vertex; i++) { var rad = (i / (vertex / 2)) * Math.PI; var lat = (R / arc_lat) * Math.sin(rad) + point.lat(); var lng = (R / arc_lng) * Math.cos(rad) + point.lng(); points[i] = new google.maps.LatLng(lat, lng); } return new google.maps.Polyline({ path: points, strokeColor: color, strokeOpacity: opacity, strokeWeight: weight }); }
定数を計算済みにしてみる?
/** * pseudoGCircle (擬似-円描画) * @param point 中心地 google.maps.LatLng * @param radius 半径 km * @param color google.maps.Polylineに渡される * @param weight google.maps.Polylineに渡される * @param opacity google.maps.Polylineに渡される * @return google.maps.Polyline */ function pseudoGCircle(point, radius, color, weight, opacity) { var vertex = 360; var TMP = 1 - 0.00669437999014132 * Math.pow(Math.sin(point.lat() * Math.PI / 180), 2); var arc_lat = 110574.2758215944444 / Math.pow(TMP, 3/2); var arc_lng = (111319.490793273333 * Math.cos(point.lat() * Math.PI / 180)) / Math.pow(TMP, 1/2); var R = radius * 1000; var points = new Array(vertex); for (i = 0; i <= vertex; i++) { var rad = (i / (vertex / 2)) * Math.PI; var lat = (R / arc_lat) * Math.sin(rad) + point.lat(); var lng = (R / arc_lng) * Math.cos(rad) + point.lng(); points[i] = new google.maps.LatLng(lat, lng); } return new google.maps.Polyline({ path: points, strokeColor: color, strokeOpacity: opacity, strokeWeight: weight } }
追記 2008/07/21 Google AJAX API ローダー対応。(G名前空間からgoogle.maps.* 名前空間へ)
追記 2018/10/08 V3に書き換え