プログラムの事とか

お約束ですが「掲載内容は私個人の見解です」

WPFで地図にTileを追加したりしてみる

f:id:puni-o:20160422140956j:plain

puni-o.hatenablog.com

puni-o.hatenablog.com

puni-o.hatenablog.com

WPF版です。(今度こそXamarin.Androidだと思った?ねぇ思った?)

サクッとやりますよ。

使うのはMicrosoft.Maps.MapControl.WPFです。Nugetでゲットできます。

f:id:puni-o:20160422141136p:plain

こんな感じ。みんな地図のスクショなので本当に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);

できました。(スクショ割愛)

次回最終回!