WPFで地図にTileを追加したりしてみる
のWPF版です。(今度こそXamarin.Androidだと思った?ねぇ思った?)
サクッとやりますよ。
使うのはMicrosoft.Maps.MapControl.WPF
です。Nugetでゲットできます。
こんな感じ。みんな地図のスクショなので本当にWPFなのか分からなくなってきましたね。今後スクショは控えます。
Open Street Mapを追加する
追加してみます。
public class CustomTileSource : TileSource { public override Uri GetUri(int x, int y, int zoomLevel) => new Uri($"http://tile.openstreetmap.org/{zoomLevel}/{x}/{y}.png"); }
TileSource
を継承してGetUri
をオーバーライドします。
って、Silverlight版と一緒ですね。コピペしちゃいましたよ。
あとはこれを追加してあげるだけ。
var tileLayer = new MapTileLayer(); tileLayer.TileSource = new CustomTileSource(); Map.Children.Add(tileLayer);
Map
はインスタンス名です。
MapTileLayer
というTile用のレイヤーがあるので、このレイヤーに先ほどのTileSourceを指定してMap
に追加です。
実行するとOpen Street Mapが地図の上に出ます。(スクショは割愛)
既定の地図を変更する
Map.Mode = new MercatorMode(); var tileLayer = new MapTileLayer(); tileLayer.TileSource = new CustomTileSource(); Map.Children.Add(tileLayer);
デフォルトの地図はMap.Modeに初期に入っている人が呼んでいるので新しいModeを作れば既定の地図が消えます。 変更ではなく既定の地図を消すという感じですがこれが楽なんでいいんじゃないでしょうか。
真面目にやるならMercatorModeを継承してちゃんと他の地図を表示できるものを作るべきでしょうね。
自分で描いたタイルの追加
TileSource
を継承したクラスを作ります。
public class OwnDrawTileSource : TileSource { public OwnDrawTileSource() { this.DirectImage = this.TileRender; } public BitmapImage TileRender(long x, long y, int zoomLevel) { var rawData = new byte[256 * 256 * 4]; var a = (byte)0x80; var r = ((x + y) % 3) == 0 ? (byte)0xFF : (byte)0; var g = ((x + y + 1) % 3) == 0 ? (byte)0xFF : (byte)0; var b = ((x + y + 2) % 3) == 0 ? (byte)0xFF : (byte)0; for (var yy = 0; yy < 256; yy++) { for (var xx = 0; xx < 256; xx++) { var index = (yy * 256 + xx) * 4; rawData[index] = r; rawData[index + 1] = g; rawData[index + 2] = b; rawData[index + 3] = a; } } var source = BitmapSource.Create(256, 256, 96, 96, PixelFormats.Pbgra32, null, rawData, 256 * 4); var result = new BitmapImage(); using (var stream = new MemoryStream()) { var encoder = new PngBitmapEncoder(); encoder.Frames.Add(BitmapFrame.Create(source)); encoder.Save(stream); stream.Position = 0; result.BeginInit(); result.CreateOptions = BitmapCreateOptions.PreservePixelFormat; result.CacheOption = BitmapCacheOption.OnLoad; result.UriSource = null; result.StreamSource = stream; result.EndInit(); } result.Freeze(); return result; } }
コンストラクタでDirectImageデリゲートにメソッドを指定して、そこでBitmapImage
を返すようにします。(BitmapImageの作り方が非常に怪しいんですけどこれでいいのかな・・・)
あとはこのクラスを指定するだけ。
var drawTileLayer = new MapTileLayer(); drawTileLayer.TileSource = new OwnDrawTileSource(); Map.Children.Add(drawTileLayer);
できました。(スクショ割愛)
次回最終回!