プログラムの事とか

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

Xamarin.iOSで地図にTileを追加したりしてみる

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

前回

puni-o.hatenablog.com

のXamarin.iOS版です。(Xamarin.Formsではありませんよ

iOSで標準の地図コントロールと言えばMKMapViewだと思うのでこれを使っていきます。当然TMSです。

とりあえずこんな感じで。(StoryboardにMKMapViewを置いただけ)

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

UWPよりきれいですね

Open Street Mapを追加する

MKTileOverlayを使います。コンストラクタにURLテンプレートを渡せばいいです。

Map.Delegate = new MapDelegate();
var osmTileOverlay = new MKTileOverlay("http://tile.openstreetmap.org/{z}/{x}/{y}.png");
Map.InsertOverlay(osmTileOverlay, 0, MKOverlayLevel.AboveLabels);

class MapDelegate : MKMapViewDelegate
{
    public override MKOverlayRenderer OverlayRenderer(MKMapView mapView, IMKOverlay overlay)
    {
        if (overlay is MKTileOverlay)
        {
            return new MKTileOverlayRenderer((MKTileOverlay)overlay);
        }
        return base.OverlayRenderer(mapView, overlay);
    }
}

MapはMKMapViewのインスタンスです。

URLテンプレートは、UWPの時は{zoomlevel}だったところが{z}に変わっているだけですね。

Overlayを追加する時はMapDelegateにOverlayRendererを追加しないといけないのが面倒だし少しはまるところですね。

iOS9(だっけ?)からはこれだけではまだ表示できないという罠があります。 上のソースのURLを見るとhttpですね、これダメなんですよね・・。

とりあえずInfo.plistに下を追加しておきましょう。

<key>NSAppTransportSecurity</key>
<dict>
    <key>NSExceptionDomains</key>
    <dict>
        <key>tile.openstreetmap.org</key>
        <dict>
            <key>NSExceptionAllowsInsecureHTTPLoads</key>
            <true/>
            <key>NSIncludesSubdomains</key>
            <true/>
        </dict>
    </dict>
</dict>

実行すると

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

こんな感じになります

既定の地図を変更する

iOSでもデフォルトの地図を入れ替えることができます。先ほどのソースに一行追加するだけ

var osmTileOverlay = new MKTileOverlay("http://tile.openstreetmap.org/{z}/{x}/{y}.png");
osmTileOverlay.CanReplaceMapContent = true;
Map.InsertOverlay(osmTileOverlay, 0, MKOverlayLevel.AboveLabels);

osmTileOverlay.CanReplaceMapContent = true;です。

カスタムソースのタイルを追加

前回と同じように半透明のタイルを重ねます

class CustomTileOverlay : MKTileOverlay
{
    public override void LoadTileAtPath(MKTileOverlayPath path, MKTileOverlayLoadTileCompletionHandler result)
    {
        UIGraphics.BeginImageContext(TileSize);
        var a = 0.5f;
        var r = ((path.X + path.Y) % 3) == 0 ? 1 : 0;
        var g = ((path.X + path.Y + 1) % 3) == 0 ? 1 : 0;
        var b = ((path.X + path.Y + 2) % 3) == 0 ? 1 : 0;
        var fill = new CGColor(r, g, b, a);
        using (var graphic = UIGraphics.GetCurrentContext())
        {
            graphic.SetFillColor(fill);
            graphic.FillRect(new CGRect(0, 0, TileSize.Width, TileSize.Height));
        }
        var tileImage = UIGraphics.GetImageFromCurrentImageContext();
        UIGraphics.EndImageContext();
        result(tileImage.AsPNG(), new NSError());
    }
}

MKTileOverlayを継承したクラスを作ってLoadTileAtPathでビットマップを作って返すようにします。

あとはこれを追加するだけ。

var customTileOverlay = new CustomTileOverlay();
Map.InsertOverlay(customTileOverlay, 0, MKOverlayLevel.AboveLabels);

実行すると

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

多少重いですができました。

多分続きます