UWPのBing Mapsトークン廃止対策とAzure Maps移行ガイド|WebView2/WinUI 3で実装する現実解

UWP の MapControl で Bing Maps の無料トークン廃止に直面し、「いま何をすれば地図機能を止めずに済むのか」「最終的にどこへ着地させるべきか」を、短期・中期・長期の観点で徹底整理しました。現実解としての Azure Maps + WebView2 を中心に、実装の具体手順、API 置き換えマッピング、セキュリティやパフォーマンス設計まで、現場でそのまま使える粒度で解説します。

目次

背景と現状課題の整理

UWP アプリで Windows.UI.Xaml.Controls.Maps.MapControl を利用していると、Bing Maps の無料トークン廃止後に “MapServiceToken not specified” の警告が表示されます。これにより、次の意思決定が必要です。

  • 短期:既存の Bing Maps Enterprise 契約があるなら延命運用を図る(ない場合は代替を即検討)。
  • 中期:UWP ではネイティブの Azure Maps コントロールが提供されていないため、WebView2 で Azure Maps Web SDK(HTML/JS)をホストする構成に移行する。
  • 長期:UWP 自体の将来性を鑑み、WinUI 3(Windows App SDK)への段階移行を進める。地図は引き続き WebView2 でホスト可能。

意思決定の全体像(短期・中期・長期の解決策)

期間解決策詳細
短期既存の Bing Maps Enterprise トークンで延命新規顧客向けの Enterprise トークン発行は原則停止。既存契約があれば既定のサポート期間まで利用可能。新規契約がない場合でも、営業経由での 短期延長(1 年単位)が成約できた事例あり(要相談)。
目の前のサービス継続を優先しつつ、すぐに中期計画を始動。
中期UWP + WebView2 で Azure Maps Web SDK をホストUWP 用のネイティブ Azure Maps コントロールは未提供。
アプリに WebView2 を組み込み、 ローカル HTML(azuremap.html)で atlas.Map() を初期化、 JS ⇄ C# の双方向通信で UI/データ連携、 REST API(検索・ルート・時空間分析)と連動。 これにより 機能差を最小化しつつリフトが可能。
長期WinUI 3 へ段階移行、地図は継続して WebView2UWP の機能追加は縮小傾向。将来的な UI/フレームワークの選好は WinUI 3。
機能単位のライブラリ化 → 新規 UI へ再利用という漸進移行を推奨。移行期間中は「UWP(WebView2+Azure Maps)」と「WinUI 3」 の併走も現実的。

UWP で Azure Maps を使う実装手順

前提条件と環境

  • OS:Windows 10 バージョン 1809 以降(または Windows 11)
  • ランタイム:Microsoft Edge(Chromium)WebView2 Runtime
  • SDK:Azure Maps Web SDK(後述のとおりローカル配置を推奨)
  • 鍵管理:アプリに鍵をハードコードしない。安全な読み出し(環境変数、保護ストレージ、Key Vault 経由など)を徹底。

手順 1:パッケージ導入(UWP)

  • Microsoft.UI.Xaml(WinUI 2)を NuGet で追加。
    UWP では WinUI 2 の WebView2 コントロール(Microsoft.UI.Xaml.Controls.WebView2)を使用します。
  • WebView2 Runtime の配布形態(Evergreen / Fixed)を設計に合わせて選定。

手順 2:XAML に WebView2 を配置

<Page
    x:Class="MyApp.Views.MapPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:muxc="using:Microsoft.UI.Xaml.Controls">


<Grid>
    <muxc:WebView2 x:Name="MapWebView"
                   HorizontalAlignment="Stretch"
                   VerticalAlignment="Stretch" />
</Grid>


 

手順 3:ローカル HTML と SDK を配置

  • Assets/azuremaps/ 配下に Azure Maps Web SDK を配置(例:atlas.min.js、atlas.min.css)。
  • Assets/azuremaps/azuremap.html を用意。
<!doctype html>
<html lang="ja">
<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="stylesheet" href="/sdk/atlas.min.css">
  <style>html,body,#map{height:100%;margin:0;padding:0;}</style>
</head>
<body>
  <div id="map"></div>




 

ポイント:HTML 内では SDK を /sdk/atlas.min.js のような仮想パスで参照しています。UWP から仮想ホストとフォルダのマッピングを行うことで、ローカル資産を HTTP ドメインとして安全に提供し、Service Worker や CORS の制約を回避します。

手順 4:C# 側で仮想ホストを張り、キーを注入

using Microsoft.UI.Xaml.Controls;
using Microsoft.Web.WebView2.Core;
using Windows.Storage;

public sealed partial class MapPage : Page
{
public MapPage()
{
this.InitializeComponent();
this.Loaded += MapPage_Loaded;
}


private async void MapPage_Loaded(object sender, Windows.UI.Xaml.RoutedEventArgs e)
{
    await MapWebView.EnsureCoreWebView2Async();

    // ローカル Assets を仮想ホストにマップ
    // https://local.azuremaps を Assets/azuremaps に対応付け
    var assetsFolder = await StorageFolder.GetFolderFromPathAsync(
        Windows.ApplicationModel.Package.Current.InstalledLocation.Path + @"\Assets\azuremaps");

    MapWebView.CoreWebView2.SetVirtualHostNameToFolderMapping(
        "local.azuremaps",
        assetsFolder.Path,
        CoreWebView2HostResourceAccessKind.Allow);

    // /sdk, /azuremap.html のようなパスに対応
    MapWebView.CoreWebView2.AddWebResourceRequestedFilter(
        "https://local.azuremaps/*",
        CoreWebView2WebResourceContext.All);

    // HTML をナビゲート
    MapWebView.Source = new Uri("https://local.azuremaps/azuremap.html");

    // セキュアに取得した Azure Maps キーを注入(例:保護ストレージから読み出し)
    var key = await LoadAzureMapsKeyAsync(); // 実装は後述
    MapWebView.CoreWebView2.ExecuteScriptAsync($"window.__setAzureMapsKey('{key}')");

    // JS→C# のメッセージ受信
    MapWebView.CoreWebView2.WebMessageReceived += (s, args) =>
    {
        var json = args.WebMessageAsJson; // { type:'map.click', position:{lat,lon} }
        // 必要に応じて座標を解析して ViewModel へ反映、逆ジオコーディングなどを実行
    };
}

private async Task<string> LoadAzureMapsKeyAsync()
{
    // 例:アプリ設定や PasswordVault / DataProtectionProvider を利用して安全に復号
    // 実際の実装ではハードコードしないこと
    return await Task.FromResult(AppSecrets.Get("AZURE_MAPS_KEY"));
}


} 

手順 5:地物描画・イベントの置き換え

MapControl のプッシュピンやポリライン、ポリゴンは、Azure Maps では DataSource+Layer(Symbol/Line/Polygon)で表現します。

// JS(地図準備完了後)
const ds = new atlas.source.DataSource();
window.__map.sources.add(ds);

// プッシュピン
ds.add(new atlas.data.Point([lng, lat]));

// ライン
ds.add(new atlas.data.LineString([[lng1,lat1],[lng2,lat2],[lng3,lat3]]));

// ポリゴン
ds.add(new atlas.data.Polygon([[[lngA,latA],[lngB,latB],[lngC,latC],[lngA,latA]]]));

// レイヤ
window.__map.layers.add(new atlas.layer.SymbolLayer(ds));
window.__map.layers.add(new atlas.layer.LineLayer(ds, null, { strokeWidth: 3 }));
window.__map.layers.add(new atlas.layer.PolygonLayer(ds, null, { fillOpacity: 0.3 })); 

API 置き換えマッピング(Bing Maps → Azure Maps)

機能(Bing Maps / MapControl)Azure Maps(Web SDK)移行ポイント
MapIcon(Pushpin)DataSource + SymbolLayerプロパティは properties.title など GeoJSON 属性へ移す。カスタム画像は imageSprite または IconOptions。
MapPolylineDataSource + LineLayerスタイルは strokeColor、strokeWidth 相当をレイヤのオプションへ。
MapPolygonDataSource + PolygonLayer穴(interior rings)は座標配列の二次元目で表現。
カスタムタイル(TileSource)TileLayerURL テンプレート({x}/{y}/{z})をそのまま指定可。サブドメイン配列にも対応。
タップ・クリックイベントmap.events.add('click', ...)地物単位なら atlas.io.feature のヒットテストか、レイヤーイベントを使用。
位置検索・逆ジオコーディングSearch REST API/search/address、/search/address/reverse を HttpClient から呼び出す。
ルーティングRoute REST API交通状況・最適化・マトリクスなどはエンドポイントで制御。
ジオフェンスSpatial / Geofenceポリゴン集合と現在地の包含判定をサービス側またはクライアントで。

REST API 連携(UWP/C#)の実装例

Azure Maps の REST は標準の HttpClient で呼び出せます。以下は住所検索の例です。

using System.Net.Http;
using System.Text.Json;

public async Task> SearchAddressAsync(string query, string subscriptionKey)
{
using var http = new HttpClient();
var url = $"[https://atlas.microsoft.com/search/address/json](https://atlas.microsoft.com/search/address/json)" +
$"?api-version=1.0&language=ja-JP&limit=10" +
$"&subscription-key={Uri.EscapeDataString(subscriptionKey)}" +
$"&query={Uri.EscapeDataString(query)}";


using var res = await http.GetAsync(url);
res.EnsureSuccessStatusCode();

var json = await res.Content.ReadAsStringAsync();
using var doc = JsonDocument.Parse(json);

var list = new List<Place>();
foreach (var item in doc.RootElement.GetProperty("results").EnumerateArray())
{
    var poi = item.TryGetProperty("poi", out var poiEl) ? poiEl : default;
    var pos = item.GetProperty("position");
    list.Add(new Place{
        Name = poi.ValueKind != JsonValueKind.Undefined ? poi.GetProperty("name").GetString() : item.GetProperty("address").GetProperty("freeformAddress").GetString(),
        Latitude = pos.GetProperty("lat").GetDouble(),
        Longitude = pos.GetProperty("lon").GetDouble()
    });
}
return list;


}

public class Place {
public string Name { get; set; }
public double Latitude { get; set; }
public double Longitude { get; set; }
} 

キー管理とセキュリティ設計

  • ハードコード禁止:リバースエンジニアリングで容易に抽出されます。
  • 安全なロード:アプリ初回起動時に暗号化された設定から復号、もしくは確立済みチャネルで短寿命トークンを配布。
  • 更新容易性:サーバー経由のローテーションを想定し、クライアントは再起動なしで差し替え可能に。
  • 権限分離:開発・検証・本番環境でキーを分離。テナントの RBAC とアラートで監視。

パフォーマンス最適化とオフライン対応

  • WebView2 でも GPU アクセラレーションは有効:レイヤ数・地物数が多い場合はクラスタリングやタイル化を検討。
  • HTTP キャッシュ:仮想ホスト(例:https://local.azuremaps/)配下で Service Worker を有効化し、SDK・スタイル・画像をキャッシュ。
    動的タイルや検索レスポンスの 完全オフラインは原則不可。要件次第でタイルサーバーや独自キャッシュ層を設計。
  • ストリーム描画:大量フィーチャは GeoJSON 一括より MVT(ベクトルタイル)を推奨。段階ズームでレイヤ切替。

UWP → WinUI 3 段階移行のすすめ

アプリの将来性を確保するため、UI・シェルを WinUI 3 に移し替える計画を並走させます。地図は当面 WebView2 を継続利用できます。以下の順序が実践的です。

  1. 機能単位で分割:ドメインサービス/ビューモデルを .NET Standard / .NET 8 ライブラリ化。
  2. 共通コンポーネント化:カスタムコントロールやビヘイビアを移植・再設計。
  3. デザイン刷新:XamlControlsGallery を参考に、入力サイズ・アクセシビリティ指標を達成。
  4. 共通 CI:UWP・WinUI 3 の両ビルドを同一パイプラインで検証。UI テストで座標・レイヤ表示の差異を検出。

サンプル:Bing MapControl API 呼び出しの「置換」チェックリスト

旧 API(Bing Maps)新 API(Azure Maps)検討事項対応状況
MapControl.Center/ZoomLevelmap.setCamera初期表示とカメラアニメーションの互換—
MapIconSymbolLayer吹き出し UI は Popup/HTML テンプレで代替—
MapPolylineLineLayerスタイル・クリックヒットの調整—
MapPolygonPolygonLayer穴ポリゴン・塗りと枠の同時制御—
交通情報(Traffic)レイヤ/データ(該当機能)可視化と凡例設計、配色ガイドライン—
検索/逆ジオ/ルートREST APIレートリミット・課金単位・リトライ—

品質保証(QA)と運用のベストプラクティス

  • 座標系・測地系の差異検証:WGS84 基準での表示誤差がないか、既存実装とスクリーンショット差分を取る。
  • テレメトリ:REST 呼び出しの成功率・レイテンシ・ズーム別フィーチャ数を可視化。閾値アラートを設定。
  • フェイルセーフ:Azure Maps 障害時に「簡易タイルのみ」「直近キャッシュのみ」など段階的劣化で運用継続。
  • キー流出対策:利用域制限・ローテーション・利用状況の日次監査。

よくある設計質問(FAQ)

Q. 新規に Bing Maps の Enterprise トークンは取得できますか?

A. 新規発行は基本停止と捉えるのが現実的です。既存契約があれば契約期間内は利用可能です。短期延長の可否は営業窓口に相談が必要です。

Q. UWP で Azure Maps をネイティブに扱う方法はありませんか?

A. 現時点の実用解は WebView2 で Web SDK をホストする方式です。レイヤ・イベント・REST 連携を組み合わせれば、多くのユースケースは移行できます。

Q. オフライン対応は可能ですか?

A. 完全オフラインは難度が高いです。要件を細分化し、タイル・スタイル・スプライトなど静的資産のキャッシュ、ルート/検索は「直近結果の再利用」や「簡易ローカル検索」で退避する戦略を取ります。業務要件によっては独自タイルサーバーや端末内データベース連携が必要です。

Q. 長期的には何を目標にすべきですか?

A. UI フレームワークの将来性・メンテ性を踏まえ、WinUI 3 へ段階移行しつつ、地図表示は WebView2 構成を継続するアーキテクチャが堅実です。

実プロジェクトでの進め方(90 日プラン例)

期間主要タスク成果物判断ゲート
Day 1–15Bing 依存点の棚卸し、Azure Maps プロトタイプ(ピン/線/検索)PoC、差分レポートWebView2 方針の確定、セキュリティ方式の合意
Day 16–45API 置換の実装、REST ラッパー、UI の最小改修ベータ版、性能測定結果レイヤ構成・キャッシュ方式の確定
Day 46–90テレメトリ・アラート、障害時劣化設計、運用 Runbookリリース版、運用手順書次期(WinUI 3)ロードマップ策定

実装時の技術メモ(重要ポイントの再掲)

  • Azure Maps キーの安全管理:サブスクリプションから取得。アプリには埋め込まず、暗号化格納や安全なチャネルで注入。
  • WebView2 の OS 前提:Windows 10 バージョン 1809 以降(または Windows 11)。
  • パフォーマンス/オフライン:GPU アクセラレーションは有効。完全オフラインは不可。必要なら独自キャッシュ・タイル層を設計。
  • WinUI 3 移行工数:UI 再設計は XamlControlsGallery を参考に。共通ビヘイビアをコンポーネント化し、UWP/WinUI 3 の両ビルドを CI で併走させるとリスクが下がります。

サンプル:仮想ホスト配下での Service Worker(任意)

SDK・画像・スタイルのキャッシュを強める場合の例です。Assets/azuremaps/sw.js を登録し、HTML から有効化します。

// azuremap.html 内
if ('serviceWorker' in navigator) {
  navigator.serviceWorker.register('/sw.js').catch(console.error);
}
// sw.js(簡易例)
self.addEventListener('install', (event) =&gt; {
  event.waitUntil(
    caches.open('azuremaps-v1').then(cache =&gt; cache.addAll([
      '/azuremap.html',
      '/sdk/atlas.min.js',
      '/sdk/atlas.min.css'
    ]))
  );
});
self.addEventListener('fetch', (event) =&gt; {
  event.respondWith(
    caches.match(event.request).then(resp =&gt; resp || fetch(event.request))
  );
});

注意:仮想ホスト(例:https://local.azuremaps/)で配信していること、かつ HTTPS スキームであることが前提です。

トラブルシューティング

  • 空白画面になる:EnsureCoreWebView2Async の完了前にナビゲートしていないか確認。仮想ホストマッピングのパスも要確認。
  • “MapServiceToken not specified” が残る:旧 MapControl の初期化コードが残っていないか。Azure Maps のキー注入タイミングを HTML 側で受け取れているか検証。
  • クリック座標が取得できない:イベントは map.events.add('click') を使用。e.position の null チェックを忘れずに。
  • REST の 401/403:購買環境のキーを使っているか、参照リージョンの整合、レート制御・時刻ずれ(署名方式時)を確認。

まとめと推奨アーキテクチャ

結論:新規の Bing Maps Enterprise トークンは基本的に入手困難であり、既存契約がある場合のみ延命が現実的です。UWP で地図機能を継続する最短ルートは Azure Maps Web SDK を WebView2 でホストする方式です。
将来の保守性・採用技術の持続性を考慮すれば、WinUI 3 への段階移行を今すぐ計画・着手し、移行期間中は「UWP+WebView2(Azure Maps)」と「WinUI 3」 を併走させるのがリスク最小です。
本記事のサンプル(仮想ホスト・キー注入・API 置換マッピング)を雛形として、最初の 2 週間で PoC、90 日以内の本番切り替えを目標にするのが現実的なロードマップです。


付録:移行判断のクイックチェック

設問Yes の場合No の場合
Bing Enterprise 契約を保持しているか?短期は延命、並行で Azure Maps 移行 PoC を直ちに開始。即時に Azure Maps(または他サービス)へ移行計画を立案。
UWP の継続運用が必須か?WebView2 で Azure Maps をホスト。WinUI 3 併走計画を作成。新規は WinUI 3 を第一候補に。UWP は保守のみに。
完全オフラインが必須か?要件分解の上、カスタムタイル・ローカル DB・同期戦略を設計。オンライン前提でキャッシュ最適化(Service Worker/HTTP キャッシュ)。

実務に効く Tips(覚えておきたい要点)

  • HTML と SDK はローカル配布が安全・高速。仮想ホストで Service Worker を使えるようにする。
  • 大量地物はサーバーで前処理(タイル化)し、クライアント負荷を軽減。
  • REST はリトライ・レート制御・タイムアウトを必ず実装。ユーザー操作にフィードバック(スピナー/再試行)。
  • UI はズーム段階でレイヤ切替(小縮尺では概略、多縮尺で詳細)。
  • 障害時は段階的劣化(最後の正常タイル・最低限ルート等)で UX を守る。

この記事を書いた人

実務の現場で詰まりがちなポイントを地図にするITブログ「IT trip」を運営。Windows/Office(Teams・Excel)からSQL、サーバ運用、ガジェットまで、再現性のある手順と“なぜそうなるか”を丁寧に解説します。読んだらすぐ試せること、そして迷った人の次の一歩が見えることを大切にしています。

コメント

コメントする

目次