引続き、溝畑です。
この記事は前回OpenLayers2で地図に線を描画してみるの続きです。
↑読んでからの方が良いかもしれないです!
前回、世界一周する線を引くと、点は思い通りに表示されているのに、線は上手く引けませんでした。
今回はここを解決していきます。
とはいえ、完璧な解決ではないのですが...。
ひとつの考え方ぐらいで捉えてもらえるとありがたいです。
前回の失敗
180度の線が超えられないサンプル前回の最後に使ったデータはこちら。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
var plot_data = [ { EW: 'E', NS: 'S', Longitude: 135.17830, Latitude: 34.67822 }, // 神戸 { EW: 'E', NS: 'S', Longitude: 139.6582, Latitude: 35.6751 }, { EW: 'E', NS: 'S', Longitude: 155.6542, Latitude: 50.2893 }, { EW: 'E', NS: 'S', Longitude: 166.6424, Latitude: 54.2893 }, { EW: 'E', NS: 'S', Longitude: 176.5433, Latitude: 58.2334 }, { EW: 'W', NS: 'S', Longitude: -178.5433, Latitude: 58.2334 }, { EW: 'W', NS: 'S', Longitude: -119.8090, Latitude: 58.28 }, { EW: 'W', NS: 'S', Longitude: -77.534, Latitude: 36.4344 }, { EW: 'W', NS: 'S', Longitude: -40.0834, Latitude: 46.823 }, { EW: 'W', NS: 'S', Longitude: -4.0843, Latitude: 51.943 }, { EW: 'E', NS: 'S', Longitude: 17.0234, Latitude: 64.2444 }, { EW: 'E', NS: 'S', Longitude: 84.0234, Latitude: 61.7243 }, { EW: 'E', NS: 'S', Longitude: 104.3443, Latitude: 44.3434 }, { EW: 'E', NS: 'S', Longitude: 121.9923, Latitude: 14.0943 }, { EW: 'E', NS: 'S', Longitude: 135.17830, Latitude: 34.67822 } // 神戸に ]; |
神戸から適当に太平洋を渡って、アメリカ大陸へ...また海を渡り一周してくるようなものでした。
変わった点といえば、東経・西経、北緯・南緯の情報を追加しています。
180度から先に線を引く(失敗列)
まず、この地図での表現は東経は0〜180度、西経は-180度まで。北緯は0〜90度、南緯は-90度まで。一周する可能性があるのは経度の方ですね。
前回の失敗した線を見ると、どうも180度を超えられずに線は戻っちゃっている様子。
超えられないなら超えさせれば良い。
ということで、超えるように西経なら+360度して180度+で表すようにしてみます。
1 2 3 4 5 6 7 8 9 10 |
var Map = { // ... plot: function () { //... $.each(plot_data, function (key, val) { if (val.EW === 'W') val.Longitude += 360; // 西経なら+360度して180度+にする // ... }.bind(this)); } }; |
さて、どうなったでしょうか。
180度は超えるサンプル
超えられなかった180度は超えられました!
問題解決...ではないですね。
今度は0度付近で同じことが起きています。
なので、この方法では解決できないことが分かります。
なぜ線が戻るのか
前回の失敗した箇所と、今回の失敗した箇所をよく見てみると、線自体は繋がっているんです。-180〜0〜180度の地図が2枚繋がっていると考えます。
仮に左を地図A、右を地図Bとします。
前回の失敗は、地図Aから見た-178度に向かって線が引かれていたから。
今回は、地図Aから見た180度+に向かって線を引いたから180度を超えられました。
0度で返ってきているのは、360度を超えたからと考えれば良いと思います。
地図に主導権があるというような、そんな雰囲気で捉えておけば良いと思います。
地図を動かすと180度付近で、ロードされるような挙動になります。そこで切り替わっています。
繋がらないなら切ればいい
失敗から分かったことを元に解決方法を考えます。繋げられないのなら、線を切ってみましょう。
見た目が繋がっていれば、途切れていようとあまり関係ですよね。
こうなれば、問題なく描ける気がします。
もうひとつ、0度と180度の間に絶対点があるとは限らないです。
こっちは、ないなら点を作れば良いという発想で進めてみます。
1次関数を使う
まさか、中学の数学を使うときがくるとは...!東経→西経 or 西経→東経となる部分で、2点を通る直線の式を求めてあげると、
180度、0度との交点から点を作ることができそうです。
赤の点を求めます。
懐かしのあの式の登場です。
y = ax + b
xは0か180で固定です。
まずはaを求めます。aは傾きなので...傾き = yの増加量 / xの増加量でした。
あとは切片を求めれば良いです。
もうひとつ、東経→西経、西経→東経が全部で4パターンあるので、実際は考慮する必要がありますが、今回使っているデータは東経→西経は180度、西経→東経は0度と決まっているので省きます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
function calcIntersection(x1, y1, x2, y2, ew) { var x, y, a, b; // 東経→西経 if (ew === 'W') { // 今回は180度でのみ。実際は0度か180度かの判定で切り替えが必要 x = 180; a = (y2 - y1) / (x2 - x1); b = y1 - a * x1; y = a * x + b; // 西経→東経 } else { // ... } return y; } |
次に、今まで線のデータを作成していたところに、東経→西経 or 西経→東経と変わるかの判定を加えて、変わっているなら、間に点を加えます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
$.each(plot_data, function (key, val) { // ... var lines = []; var next = null; // 次のポイントのデータ if (key < plot_data.length - 1) { next = plot_data[key + 1]; } if (next !== null) { var y = calcIntersection(val.Longitude, val.Latitude, next.Longitude, next.Latitude, val.EW); // 東経→西経 if (val.EW === 'E' && next.EW === 'W') { line_data.push(/* 180度と求めたy座標でポイント生成 */); // 点ができたのでここで一旦線をつくる lines.push(/* line_dataを線にする */); line_data = []; // 次は-180度から始める line_data.push(/* -180度と求めたy座標でポイント生成 */); // 西経→東経 } else if (val.EW === 'W' && next.EW === 'E') { // ... } } }); |
線を途切れさせたサンプル
一番地図を縮小したときには、途切れた部分が見えてしまうのですが、拡大しつつ地図をスクロールさせると、上手く繋がっているように見えるようになりました。
見た目は良いのですが、例えば、「線にマウスを当てたときに太さを変えたい」などという場合には、線が複数になっているので、少し苦労します。
考え方を変えることで、解決策が見えてきたりするので、OpenLayers2で上記の悩みがある場合は、参考程度にしていただければと思います。
もっと簡単な方法があるよ!という場合は、是非教えていただければ幸いです。。。
地図が続いたので、次回はHighcharts(Highstock)を使ったグラフ描画あたりを書こうかと思います。
キー・ポイントでは、ブラウザの地図で、
世界一周の軌跡を残したいエンジニアを募集しています。
・2017新卒の方はリクナビ2017へ(タイムリーに書類選考 受付中!)
・中途の方はコチラへ