UWPで地図にTileSourceを追加したりしてみる
してみます
UWPに地図を出すのは@okazukiさんのブログ
を見てください。私が説明するよりわかりやすいと思うので割愛します。
とりあえずこんな感じなところまで準備します。日本が残念な感じになっているのがいい感じですね。 (そういえばVisual Studio Update2でデバッグをするとヴィジュアルツリーをいじる系のツールバーっぽいのが出るんですね)
Open Street Mapを追加する
UWPの地図(に限らず最近の地図)はTMS(Tile Map Service)を使います。(TMSがどこを指しているのかいまいちわかってません) TMSの詳しいことはググってください。地図をx,y,z(zoom)で指定できる256×256ピクセルのタイル画像にして並べていると思えばいいんじゃないでしょうか。 (256ピクセルは固定ではないようですが256以外のサイズを見たことが無いので固定でいいんじゃない?)
ということでTMSで有名なOpen Street MapをMapのTileSourceに追加してみます
こちら Slippy map tilenames - OpenStreetMap Wiki にあるstandardスタイルを使ってみます。
URL templateは
http://[abc].tile.openstreetmap.org/zoom/x/y.png
と書いてありますね。
このサーバーから地図画像をもってきて地図コントロールに重ねてみます。 コードはこんな感じ
var osmTileSource = new HttpMapTileDataSource("http://tile.openstreetmap.org/{zoomlevel}/{x}/{y}.png"); var tileSource = new MapTileSource(osmTileSource); Map.TileSources.Add(tileSource);
Map
はMapControlのインスタンスです。
HttpMapTileDataSource
がHTTP経由でタイル画像を取得してくれる人です。引数にURLのテンプレートを書きます。({x}
{y}
{zoomlevel}
がUWPのAPIが置換する部分になります)
UriRequested
をハンドルすると呼び出している状況がわかるのでお好みでどうぞ。
実行すると
こんな感じになります。残念だった日本が残念じゃなくなったぞ!
カスタムソースのタイルを追加
ローカル記憶域ってのもありますがほとんど使い道無い気がするのでそっちはパス。
カスタムソースでは自分でBitmapを作ります。
var customDataSource = new CustomMapTileDataSource(); customDataSource.BitmapRequested += CustomDataSource_BitmapRequested; var customTileSource = new MapTileSource(customDataSource); Map.TileSources.Add(customTileSource); private async void CustomDataSource_BitmapRequested(CustomMapTileDataSource sender, MapTileBitmapRequestedEventArgs args) { var deferral = args.Request.GetDeferral(); var a = (byte)0x80; var r = ((args.X + args.Y) % 3) == 0 ? (byte)0xFF : (byte)0; var g = ((args.X + args.Y + 1) % 3) == 0 ? (byte)0xFF : (byte)0; var b = ((args.X + args.Y + 2) % 3) == 0 ? (byte)0xFF : (byte)0; var bytes = new byte[256 * 256 * 4]; for (var y = 0; y < 256; y++) { for (var x = 0; x < 256; x++) { var index = (y * 256 + x) * 4; bytes[index] = r; bytes[index + 1] = g; bytes[index + 2] = b; bytes[index + 3] = a; } } var randomAccessStream = new InMemoryRandomAccessStream(); var stream = randomAccessStream.GetOutputStreamAt(0); var writer = new DataWriter(stream); writer.WriteBytes(bytes); await writer.StoreAsync(); await writer.FlushAsync(); args.Request.PixelData = RandomAccessStreamReference.CreateFromStream(randomAccessStream); deferral.Complete(); }
こんな感じ。BitmapRequested
イベントでビットマップデータを入れてあげます。
今回のソースではX座標とY座標からRGBを作って半透明なビットマップを作って返しています。
実行すると
半透明のタイルが元の地図の上に重なって表示されていることがわかりますね。
既定の地図を変更する
上のスクリーンショットでもわかるように普通に追加するとデフォルトの地図の上に、追加したTileSourceが置かれます。重ねるのではなくデフォルトの地図を入れ替えることもできます。
var osmTileSource = new HttpMapTileDataSource("http://tile.openstreetmap.org/{zoomlevel}/{x}/{y}.png"); var tileSource = new MapTileSource(osmTileSource); tileSource.Layer = MapTileLayer.BackgroundReplacement; Map.Style = MapStyle.None; Map.TileSources.Add(tileSource);
MapTileSource.LayerをBackgroundReplacementに、Map.StyleをNoneにします。 これでOpenStreetMapだけを表示するようになります。
分からなくもないよくわからない現象
上のスクショももっとズームした方が見栄えがいいんですがあえてあのレンジで撮ってます。というもの日本である程度ズームすると追加したタイルソースが表示されなくなるんですよね。
出てる
ZoomLevelが8を超える(?)と消えます。
台湾を表示すると出ている地域と出ていない地域の境界線がわかります。
そういうことですか・・・
北朝鮮が出ていて韓国や日本が出ていないのがよくわからないんですがどういうルールなんでしょうかね・・・(長方形の領域でのとばっちりとかそんな感じでもなさそうだし)
上でMapTileSource.Layerを変更しましたが、デフォルトではこの値はRoadOverlay
になっています。
定義はこんな感じ。
public enum MapTileLayer { LabelOverlay = 0, RoadOverlay = 1, AreaOverlay = 2, BackgroundOverlay = 3, BackgroundReplacement = 4 }
先ほどのgif動画を見るとラベル以外(Road/Area/Background(も?))が表示されていないようなのでMapTileSource.Layerの値を変更します。
var customDataSource = new CustomMapTileDataSource(); customDataSource.BitmapRequested += CustomDataSource_BitmapRequested; var customTileSource = new MapTileSource(customDataSource); customTileSource.Layer = MapTileLayer.LabelOverlay; Map.TileSources.Add(customTileSource);
表示されました。今まではラベルの下にあったタイルがラベルの上に来てしまっていますが出ないよりましでしょう。
日本をズームするとTileSourceが表示されないっていうのは問題アリだと思うんですがどうなんでしょう・・・?