Azure Maps Web SDK の最新の更新でのデータドリブンのスタイリングとその他
Posted on
12 min read
Azure Maps Web SDK の最新の更新についてお知らせします。チームは夏の間頑張って働いて、多くの強力な機能を追加し、Web Maps SDK の全体的なパフォーマンスを向上させました。この更新で追加された主要な機能強化としては次のようなものがあります。
- 開発者重視の API の機能強化
- 新しいデータ ソースとレイヤー モデル
- データ ソースへの複数レイヤーの接続
- 管理しやすい新しい Shape クラス
- レイヤーのデータドリブンのスタイリング
- 空間数値演算ライブラリ
- 地理空間的に正確な円のサポート
開発者重視の API の機能強化
API インターフェイスを強化し、いっそう直感的で高パフォーマンスにしました。これまでは、SDK の機能の大部分は Map クラスの関数として公開されていました。関連する関数がグループ化されていないため、開発者は、必要な関数を見つけるために、SDK のほとんどすべての関数とプロパティを探さなければならないことがよくありました。このリリースでは、関連する多くの機能をグループ化し、Map クラスのマネージャー プロパティで公開するようにしました。最後のバージョンでは、Map クラスには 30 以上の関数があり、そのうちの 7 つはデータ レイヤーに関係がありました。この更新では、これらの機能は、レイヤーを使いやすくする他の多くの機能と共に、マップの "レイヤー" プロパティによって公開されるようになっています。また、制御、イベント、マップ イメージ スプライト、HTML マーカー、データ ソースに関するマネージャー プロパティも、マップに追加されました。
同様に、マップ SDK の既定の設定の多くを SDK のルート atlas 名前空間に直接設定するためのオプションも追加しました。これにより、ページ上に作成されるすべてのマップ インスタンスと、作成されるすべてのサービス クライアントでは、これらの設定が既定値として使用されます。以前は、アプリケーションに追加されるすべてのマップ インスタンスとサービス クライアントで、その機能の初期化の間に、Azure Maps キーを渡す必要がありました。つまり、この同じ設定をアプリケーション内で何回も行う必要があり、面倒な場合がありました。ルート atlas 名前空間でキーを設定することにより、アプリケーション内で必要な指定が 1 回だけで済み、将来必要な場合の更新が簡単になります。
atlas.setSubscriptionKey('Your Azure Maps Key');
言語やユーザーのリージョン情報などの他の複数の設定も、ルート atlas 名前空間で既定値として設定できます。これらはマップ インスタンスまたはサービス クライアントを初期化するときに同等のオプションが指定されていない場合に使用される既定値であることに注意することが重要です。たとえば、マップを初期化するときに異なる言語を渡した場合、代わりにその言語が使用されます。これは、ページに異なる言語で複数のマップを表示する可能性があるシナリオにおいて便利です。
これらの開発者重視の改善により、Azure Maps でのアプリケーション開発が容易になるだけでなく、パフォーマンスが向上し、アプリケーションの作成に必要なコードの量も減ります。
新しいデータ ソースとレイヤー モデル
従来のマップ コントロールでは、GeoJSON 形式のベクター空間データをマップに追加することだけができました。このデータは、バックグラウンドで独自のデータ ソースを作成して管理するレイヤーに追加されました。レイヤーのデータを変更するには既存のレイヤーを上書きする必要があったため、非効率的であり、以下のサンプル コードを見るとわかるように理解しにくいコードが必要です。
var myPins = [/* ピン データを設定する配列 */]; // ピンをマップに追加する。 map.addPins(pins, { name: 'MyPinLayer' }); // レイヤーに追加する新しいピンを作成する。 var pin = new atlas.data.Feature(new atlas.data.Point(lon, lat)); // ピンの配列にピンを追加する。 myPins.push(pin); // レイヤーのすべてのデータを上書きすることによってレイヤーを更新する。これはわかりにくく、パフォーマンスが低下する。 map.addPins(pins, { name: 'MyPinLayer', overwrite: true });
このリリースでは、データ ソースとレイヤーが分離されました。これにはいくつものメリットがあり、たとえば、データ ソースのインスタンスを 1 つだけ維持し、複数のレイヤーを使用して単一のデータ ソースをレンダリングできるので、メモリの使用量が減り、パフォーマンスが向上し、次に示すように作成される API がはるかに理解しやすくなります。
// データ ソースを作成してマップに追加する。 var dataSource = new atlas.source.DataSource(); map.sources.add(dataSource); // データ ソース内のシェイプをレンダリングする方法を定義するレイヤーを作成し、それをマップに追加する。 var myPinLayer = new atlas.layer.SymbolLayer(dataSource); map.layers.add(myPinLayer); // ピンをデータ ソースに追加する。 dataSource.add([/* ピン データを設定する配列 */]); // マップに追加する新しいピンを作成する。 var pin = new atlas.data.Feature(atlas.data.Point([lon, lat])); // ピンをデータ ソースに追加すると、マップは可能の最も効率的な方法で自動的に更新する。 dataSource.add(pin);
GeoJSON 形式のデータ用に DataSource クラスを作成するだけでなく、新しい VectorTileSource クラスによるベクター タイル サービスのサポートも追加しました。これらのデータ ソースを、マップへのデータのレンダリング方法を定義している以下のレイヤーに関連付けることができます。
- バブル レイヤー – ピクセル半径を使用して、スケーリングされた円として点データをレンダリングします。
- 線レイヤー – 線と多角形の輪郭をレンダリングします。
- 多角形レイヤー – 多角形の塗りつぶされた領域をレンダリングします。
- シンボル レイヤー – 点データをアイコンとテキストとしてレンダリングします。
また、ラスター タイル化された画像をマップの上に重ね合わせることができる TileLayer クラスもあります。このレイヤーをデータ ソースに関連付けるのではなく、レイヤーのオプションとしてタイル サービス情報を指定します。
この新しいデータ ソースとレイヤー モデルを SDK に作成する一方で、マップにデータを視覚化するための関数機能とレンダリング オプションも 2 倍以上にしました。
データ ソースへの複数レイヤーの接続
前述したように、同じデータ ソースに複数のレイヤーを関連付けられるようになっています。これは奇妙に思えるかもしれませんが、さまざまなシナリオでこの機能が役に立ちます。たとえば、多角形描画エクスペリエンスを作成するシナリオについて考えます。ユーザーが多角形を描画できるようにするには、ユーザーがマップに点を付かしているときに、塗りつぶし多角形領域をレンダリングする必要があります。多角形の輪郭を描画するスタイル指定された線を追加すると、描画されている多角形のエッジが見やすくなります。最後に、多角形内の各位置に何らかの種類のハンドル (ピンやマーカーなど) を追加すると、個別の各位置を編集しやすくなります。このシナリオを示す画像を次に示します。
ほとんどのマッピング プラットフォームでこれを実現するには、多角形オブジェクト、線オブジェクト、および多角形内の各位置に対するピンを作成する必要があります。多角形が変更されたら、線とピンを手動で更新する必要があります。これを行うために必要な作業は、たちまち複雑になります。
Azure Maps で必要なものは、次のコードで示すように、データ ソース内の 1 つの多角形だけです。
// データ ソースを作成してマップに追加する。 var dataSource = new atlas.source.DataSource(); map.sources.add(dataSource); // 多角形を作成してデータ ソースに追加する。 dataSource.add(new atlas.data.Polygon([[[/* Coordinates for polygon */]]])); // 多角形の塗りつぶされた領域をレンダリングするための多角形レイヤーを作成する。 var polygonLayer = new atlas.layer.PolygonLayer(dataSource, 'myPolygonLayer', { fillColor: 'rgba(255,165,0,0.2)' }); // 多角形の輪郭のレンダリングをさらに細かく制御できるように、線レイヤーを作成する。 var lineLayer = new atlas.layer.LineLayer(dataSource, 'myLineLayer', { color: 'orange', width: 2 }); // 多角形の頂点をスケーリングされた円としてレンダリングするためのバブル レイヤーを作成する。 var bubbleLayer = new atlas.layer.BubbleLayer(dataSource, 'myBubbleLayer', { color: 'orange', radius: 5, outlineColor: 'white', outlineWidth: 2 }); // すべてのレイヤーをマップに追加する。 map.layers.add([polygonLayer, lineLayer, bubbleLayer]);
実際に操作できる例をご覧ください。
管理しやすい新しい Shape クラス
Azure Maps Web SDK のベクターに基づくデータはすべて GeoJSON オブジェクトで構成され、最終的には定義されているスキーマに従う JSON オブジェクトに過ぎません。GeoJSON データを使用するときの制限の 1 つとして、データを変更する場合、マップのオブジェクトを削除して置き換えるまで、マップはその変更を認識しません。処理を簡単にしてわかりやすくするため、任意の GeoJSON 機能またはジオメトリをラップできる新しい Shape クラスが追加されました。このクラスの関数を使用すると、GeoJSON データを簡単に更新し、Shape が追加されているデータ ソースに変更をすぐに反映できます。これが非常に便利であることがわかったため、DataSource クラスに追加されるすべての GeoJSON オブジェクトを自動的にタップするようにしてあります。
例として、マップ上のデータ ポイントの位置を更新するシナリオについて考えます。以前は、レイヤー内のデータを別に管理し、次のコードで示すようにレイヤーを上書きする必要がありました。
// ポイント機能からピンを作成する。 var pin = new atlas.data.Feature(new atlas.data.Point([-110, 45])); // マップにピンを追加する。 map.addPins([pin], { name: 'MyPinLayer' }); // ピンの座標を更新する。マップは更新されない。 pin.geometry.coordinates = [-120, 30]; // レイヤー内のすべてのピンを上書きして、マップを更新する。 map.addPins([pin], { name: 'MyPinLayer', overwrite: true });
これは直感的ではなく、さらに多くの作業を行う必要があります。Shape クラスでデータ ポイントをラップすることにより、次のコードで示すように、マップ上のデータ ポイントの位置の更新が、1 行のコードだけで済みます。
// データ ソースを作成してマップに追加する。 var dataSource = new atlas.source.DataSource(); map.sources.add(dataSource); // データ ソース内のシェイプをレンダリングする方法を定義するレイヤーを作成し、それをマップに追加する。 var myPinLayer = new atlas.layer.SymbolLayer(dataSource); map.layers.add(myPinLayer); // ピンを作成し、Shape クラスでラップして、データ ソースに追加する。 var pin = new atlas.Shape(new atlas.data.Point([-110, 45])); dataSource.add(pin); // ピンの座標を更新すると、マップが自動的に更新される。 pin.setCoordinates([-120, 30]);
ヒント: 各オブジェクトを個別にラップする代わりに、データの Shape でラップされたバージョンをデータ ソースから簡単に取得できます。
レイヤーのデータドリブンのスタイリング
この更新での重要な新機能は、プロパティ関数での新しいデータドリブン スタイリング機能です。これにより、個別のスタイリング オプションにビジネス ロジックを追加することができ、関連付けられているデータ ソースの個別の各シェイプで定義されているプロパティが考慮されます。レイヤーをレンダリングするときは、ズーム レベルを考慮することもできます。データドリブンのスタイルを使用すると、if ステートメントを使用してマップ イベントを監視するこの種のビジネス ロジックを記述して定義するために通常必要になるコードの量が大幅に減ります。
たとえば、地震データについて考えてみます。各データ ポイントにはマグニチュードのプロパティがあります。各データ ポイントに関連するマグニチュードをマップ上に表示するためBubbleLayer を使用してスケーリングされた円を描画し、データ ポイントのマグニチュードが大きいほど、円の半径を大きくします。次のコードでは、データドリブンのスタイルを BubbleLayer の radius オプションに適用する方法を示します。radius は各データ ポイントの magnitude プロパティに基づいてスケーリングされ、マグニチュードが 0 のときの 2 ピクセルから、マグニチュードが 8 のときの 40 ピクセルまで線形に変化します。
var earthquakeLayer = new atlas.layer.BubbleLayer(dataSource, null, { radius: ['interpolate', ['linear'], ['get', 'magnitude'], 0, 2, 8, 40 ] });
また、各円の色を定義して次のようなマップを生成する似たようなデータドリブンのスタイルも適用します。
実際に操作できる例をご覧ください。
空間数値演算ライブラリ
新しい空間数値演算ライブラリが atlas.math 名前空間に追加されました。これは、多くのマップ アプリケーションで一般に必要な役に立つ計算のコレクションを提供します。一部の機能では、次の計算を行うことができます。
- 位置間の直線距離。
- 線またはパスの長さ。
- 位置の間の方向。
- 距離の変換。
- 基本スプライン。一連の点の間の滑らかな曲線パスを計算できます。
- 測地パス。地球の曲率を考慮した、2 点間の直線のパスです。
- パスに沿った中間位置。
実際に操作できる例をご覧ください。
このライブラリでは、多くのよく使われる簡単な空間計算が提供されます。幾何学的な和集合や交差などのさらに高度な空間計算が必要な場合は、オープン ソースの Turf.js ライブラリが役に立つ可能性があります。Turf.js は、Azure Maps でのすべてのベクター データに対する基本形式である GeoJSON データを直接操作するように設計されており、Azure Maps で使いやすくなっています。
地理空間的に正確な円のサポート
GeoJSON スキーマでは、地理空間的に正確な円を定義するための標準化された方法は提供されていません。そのため、Azure Maps チームは、ドキュメントで示されているように、スキーマを壊すことなく GeoJSON で地理空間的に正確な円を定義するための一般的な方法を標準化しました。Azure Maps Web コントロールで純粋な JSON を使用して、または atlas.data 名前空間のヘルパー クラスを使用して、GeoJSON オブジェクトを定義できます。シアトルの上に半径 1,000 メートルの円を定義する方法の例を次に示します。
純粋な JSON を使用
var circle = { "type": "Feature", "geometry": { "type": "Point", "coordinates": [-122.33, 47.6] }, "properties": { "subType": "Circle", "radius": 1000 } };
atlas.data 名前空間のヘルパー クラスを使用
var circle = new atlas.data.Feature(new atlas.data.Point([-122.33, 47.6]), { subType: "Circle", radius: 1000 });
これらの円をレンダリングするとき、Azure Maps Web コントロールは、この Point 機能を円形の多角形に変換します。この多角形は、異なるレンダリング レイヤーの多くで使用できます。円が塗りつぶされた多角形としてレンダリングされているマップを次に示します。
実際に操作できる例をご覧ください。
地理空間的に正確な円と、BubbleLayer によって生成される円の重要な違いの 1 つは、バブル レイヤーでは各バブルに対してピクセル半径が割り当てられることです。ユーザーがマップをズームしても、ピクセル半径は変化せず、したがってバブルによってカバーされているマップの領域が変化します。地理空間的に正確な円は、その頂点がマップ上の座標にバインドされています。マップがズームされると、円はスケーリングして、カバーされている領域を維持します。
マップで使用されているメルカトル図法のため、これらの円が円形に表示されない場合があることに注意することが重要です。実際、円が北極点または南極点に近づくほど、表示はいっそう大きく楕円形になりますが、地球上でそれが表している領域は円形です。次のマップで示されている円は、どちらも半径が 750 Km (750,000 メートル) です。1 つの円は北極点に近いグリーンランドの上にレンダリングされ、もう 1 つの円は赤道に近いブラジルの上にレンダリングされています。
実際に操作できる例をご覧ください。
下位互換性
Azure Maps を使用して既にアプリを開発している場合、アプリケーション全体を書き直す必要があるかどうかが気になるでしょう。答えはノーです。下位互換性が維持されるように頑張りました。古い関数の多くは、今後のアプリケーションで開発者が使用しないように、ドキュメントでは非推奨として指定されていますが、これらの機能は SDK のバージョン 1 なので引き続きサポートされます。
ただし、マップ コントロールを使用するときの重要なステップを省略しているアプリケーションを見かけます。マップ コントロールのインスタンスを作成するときは、Web-GL キャンバスなどのいくつかのリソースを読み込む必要があります。これは非常にすばやく行われますが、非同期に処理されるので、マップ インスタンスを作成するコードの次にあるコード行は、マップの読み込みが完了する前に呼び出される可能性があります。マップが読み込まれる前に、そのコード行がマップと対話しようとすると、エラーが発生する場合があります。これを解決するには、"読み込み" イベントをマップにアタッチし、マップが読み込まれた後で実行する必要のある機能を、イベントのコールバックに追加する必要があります。マップの読み込みイベントを使用していない場合、現在のアプリケーションではほとんどの場合に正常に動作するかもしれませんが、別のユーザーのデバイスではそうならない可能性があります。問題が発生するコードとその解決方法を次に示します。
問題
// マップ インスタンスを初期化する。 var map = new atlas.Map('myMap', { 'subscription-key': 'Your Azure Maps Key' }); // マップと対話する別のコード。マップの読み込みがまだ終わっていない可能性がある。
古い API インターフェイス (まだサポートされている) を使用する解決策
// マップ インスタンスを初期化する。 var map = new atlas.Map('myMap', { 'subscription-key': 'Your Azure Maps Key' }); // マップ リソースが完全に読み込まれるまで待つ。 map.addEventListener("load", function (e) { //Add your additional code that interacts with the map here. });
新しい API インターフェイスを使用する解決策
// 自分の Azure Maps サブスクリプション キーをマップ SDK に追加する。 atlas.setSubscriptionKey('Your Azure Maps Key'); // マップ インスタンスを初期化する。 var map = new atlas.Map('myMap'); // マップ リソースが完全に読み込まれるまで待つ。 map.events.add('load', function (e) { //Add your additional code that interacts with the map here. });
ご意見をお聞かせください
チームは Azure Maps プラットフォームの発展と向上を常に目指して働いており、お客様からのご意見をお待ちしています。
- 要求したい機能がありますか。その場合は、フィードバック サイトで要求を追加するか、要求に投票してください。
- マップ データに問題が見つかりましたか。その場合は、TomTom の Map Share Reporter ツールを使用してデータ プロバイダーに直接連絡してださい。
- 問題が発生してコードが動きませんか。Azure ブログで取り上げて欲しいトピックがありますか。その場合は、Azure Maps フォーラムでご連絡ください。Microsoft がサポートします。Microsoft はお客様が Azure Maps プラットフォームを活用されることを望んでいます。
- コード サンプルをお探しですか。または、他のユーザーと共有したい優れたコードをお持ちですか。GitHub にご参加ください。