私の質問は、オフラインの Phonegap アプリケーションで大きなカスタム マップを効率的に表示し、古いモバイル デバイスをサポートしながらスムーズにパンとズームを行う方法です。
私は、地理位置情報を使用して、ユーザーが信号を持たないためインターネットに接続されていない可能性が高い遠隔地の歩行ルートをナビゲートするモバイル アプリケーションを開発しています。アプリが Android 2.2 以降 (SVG はオプションではありません) と iOS4 以降でうまく動作することが重要です。
各ルートに適した解像度で Adobe Illustrator を使用してカスタム ベクター マップを描画しました。平均は約 2000x2000 ピクセルで、これまでのところ最大のものは 4000x2400 ピクセルの画像になります。
私がネイティブではなく Phonegap/JQM を使用することにしたのは、単純に私が Web プログラミングのバックグラウンドを持っており、ネイティブ コードを深く掘り下げなくてもユーザー インターフェイスを起動して実行するための最速の方法に思えたからです。電源と画面管理のために、ネイティブ コードを使用していくつかの Phonegap プラグインを作成しました。
アプリケーションでは、ユーザーがマップをパン (ドラッグ) し、元の画像サイズの約 25% から 200% の間でズームイン/ズームアウト (ピンチ) できるようにする必要があります。
私が行ったテストのほとんどは、Android 2.3.3 を実行する HTC Desire と Android 2.2 を実行する HTC Wildfire で行われました。これらは、アプリを実行する必要がある最も低いスペックのデバイスである可能性が高いからです。
マップを表示するさまざまな方法を試してみましたが (詳細は後述)、アプリのメモリ使用量が多すぎる、必要なストレージ スペースによってアプリがダウンロードするには大きすぎる、または、 CPU 使用率が高すぎて、パン/ズーム時に遅延が発生します。
どんな提案でも大歓迎です。前もって感謝します。
私が試したアプローチ:
1. タグを使用してマップをラスター PNG として表示します
これは私が試した最初のアプローチでした。Illustrator から 4000x2400 ピクセルの画像を 128 色の PNG-8 としてエクスポートすると、746Kb のファイルになりました。ビューポートに対して相対的に画像を絶対的に配置して画像をパンし、タグの幅/高さ属性をスケーリングして画像をズームしました。このアプローチの問題は、1:1 のズーム レベルでも、Android アプリケーションが画像に 60Mb の RAM を使用し、200% にズームインすると 120Mb が増加し、HTC Wildfire でアプリがクラッシュすることでした。
2. HTML5 キャンバスを使用してラスター PNG の一部を表示する
拡大するとメモリ使用量が比例して増加するという問題を回避するために、JS を介して画像を読み込んでから、表示する画像の一部をビューポートのサイズのキャンバスにコピーしてみました。
var canvas = $(‘canvas#mycanvas’);
canvas.width = $(window).width;
canvas.height = $(window).height;
...
var img = new Image();
img.src = “map.png”;
...
var context = canvas[0].getContext("2d");
context.drawImage(img, x, y, w, h, 0, 0, canvas.width, canvas.height);
ここで、x、y はパンによって定義されるソース イメージ内の左上隅であり、w、h はズーム レベルによって決定されるソース イメージ内の領域サイズです。
ここでの問題は、大きなマップ画像がメモリ内にある間に何らかの形で品質が低下し (ディザリングが発生するメモリ上限があるとしか思えません)、アプリでマップが歪んで見えることでした:スクリーンショットの例については、こちらを参照してください。
3. HTML5 キャンバスを使用してマップをベクトルとして表示する
少しグーグルで検索した結果、アートワークを HTML5 キャンバスに表示されるベクターとしてエクスポートできる Illustrator プラグインである ai2canvas を発見しました。エクスポートの結果は、イラストレーターのすべてのパスをベジェ曲線として表す JS のチャンクを含む html ファイルです。4000x2400 マップをエクスポートすると、ベクター パスを含む 550Kb の html ファイルが作成されました。私のテスト アプリでは、マップ全体を 4000x2400 ピクセルのインメモリ キャンバス (DOM に接続されていない) にレンダリングし、context.drawImage() を使用してその関連部分をビューポート サイズのキャンバスにコピーしました。ソースとしてのメモリ キャンバス。HTC Wildfire では、メモリ内キャンバスへのすべてのベジエ カーブの最初のレンダリングに約 2000 ミリ秒かかりましたが、キャンバス間のコピーは十分に高速で、スムーズなパンとズームが可能でした。
ベクター マップを使用して 2 番目のアプローチを試みました。すべてのベクトルを大きなメモリ内キャンバスにレンダリングする代わりに、各ドラッグ/ピンチ イベント中に現在のパン位置/ズーム レベルでビューポート内に表示されているベクトル パスをアプリで計算し、表示されているベクトルのみをビューポートに描画するようにしました。 -サイズのキャンバス。これにより、必要なメモリ使用量が 10Mb に減少しましたが、すべてのドラッグ/ピンチ サイクルでこれらの計算を実行するために必要な CPU サイクルにより、古い Android フォンではアプリが非常に遅くなり、使用できなくなりました。
4. オフライン タイルを使用してマップを表示する
map tilerを使用して、25% から 100% のズーム レベルでマップの PNG タイルを作成しました。私のテスト アプリでは、オンデマンドでタイルを遅延ロードしてメモリ使用量を減らし、HTC Wildfire でもスムーズなパン/ズーム エクスペリエンスを実現できました。生成された APK のサイズを見るまでは、解決策を見つけたと思っていました。私の 4000x2400 マップの場合、マップ タイラーは 4Mb のタイル イメージを生成しました。私のマップのほとんどは約 2000x2000 ピクセルで、約 2Mb のタイルになります。私の適切なアプリケーションのコードに Phonegap のオーバーヘッドを加えると、さらに 2Mb になります。
私の意図は、Android/Apple 市場で利用可能な一連のアプリをリリースすることです。それぞれに約 10 個のマップのセットがありますが、タイリングを使用すると、各マップの重量が 1 ~ 4Mb になるため、結果として得られるアプリは非常に大きなダウンロードになります。