昨日会社で、同僚に経緯度と表示範囲から、地図ズーム逆算する方法聞かれたんですよ。
で、偉そうに、メルカトル図法だから緯度が影響するからとかなんとか言ったんですけどね、なんかいざ計算してみると、緯度関係なく計算できそうなんですよ。
で、よくよく考えると、等角図法なんで緯度に関係なく経度間の幅は一定になるはずで、それとピクセル間の比でズーム決まるはずですから、どう考えても緯度関係なさそうなんですよね。
今までよくよく考えてなかったから気づきませんでしたけど。
いやあ、これだけ地図に詳しくなったつもりでも、いろいろ気づかされるもんですね…。
というわけで、ちょっと算出法をまとめてみたら、チョー簡単な式になりました。
まず、ズームレベルzと、そのズームレベルでの球面メルカトル座標(単位m)とピクセルの比(meter per pixel:正式用語ではありませんが、mppとでも略します)の定義を見てみましょう。
JavaScriptベースだとこの辺に以前まとめましたが、数式ベースだと、
M_{max}:球面メルカトル座標最大値 (赤道での地球半周のm)\\
R:地球半径\\
\\
とおくと、M_{max}=\pi R \\
また、ズームレベルzでの世界全体地図の1辺ピクセル数は、\\
256 \times 2^z なので、mppは、\\
mpp = \frac{2\pi R}{256 \times 2^z} = \frac{\pi R}{128 \times 2^z}
また、地図表示エリアの東端経度Lx1、ピクセルx座標Px1、西端経度Lx2、ピクセルx座標Px2とおくと、表示領域から求まるmppは、
東端メルカトルx座標 M_{x1} = \frac{L_{x1} \pi R}{180}\\
西端メルカトルx座標 M_{x2} = \frac{L_{x2} \pi R}{180}\\
\\
mpp = \frac{M_{x1}-M_{x2}}{P_{x1}-P_{x2}}\\
=\frac{\frac{\pi R (L_{x1}-L_{x2})}{180}}{P_{x1}-P_{x2}}\\
経度差を\Delta L = L_{x1}-L_{x2}、\\
ピクセル差を\Delta P = P_{x1}-P_{x2}とおくと、\\
mpp = \frac{\pi R \Delta L}{180 \times \Delta P}
この両者が一致するzを求めるので、
\frac{\pi R}{128 \times 2^z} = \frac{\pi R \Delta L}{180 \times \Delta P}\\
2^z = \frac{180 \times \Delta P}{128 \times \Delta L} = \frac{45 \times \Delta P}{32 \times \Delta L}\\
z = log_2(\frac{45 \times \Delta P}{32 \times \Delta L}) \\
= log_2(1.40625\frac{\Delta P}{\Delta L}) \\
= \frac{log_e(1.40625\frac{\Delta P}{\Delta L})}{log_e 2} (対数法則詳しくない人のために)
という感じの計算結果になると思います。
つまり、JavaScriptで書くなら、
var zoom = Math.Log( 1.40625 * (px1 - px2) / (lon1 - lon2) ) / Math.LN2;
という感じでしょうか。
小数になるので、適宜ceil/floor/roundしてください。
また、Retinaだのxxhdpiだのみたいな高解像度環境では、実ピクセル数ではなく、dpやpointのような値を使うのが吉です。