ダーヤス.com プレミアム

ダーヤス.comにようこそ。プレミアムな情報で、ワークライフバランスの充実を図りませんか。

[swift]福井県鯖江市つつじバスの現在位置をMapKitの地図にリアルタイム表示するアプリ

      2016/03/27

[Swift]鯖江市コミュニティバスつつじバス

当ブログは福井県鯖江市を応援するブログですが、つつじバスロケーションWEB APIなるオープンデータを見つけましたので、それを利用してつつじバスの情報をマッピングするアプリを作ってみようかなと思ったりしました。

まずバス停でも地図表示しようかなと思ったところ、バス停座標データを取得するには、リクエスト内のURLに「路線番号」を埋め込まなければならないらしい。

路線番号データを取得

というわけで、まずは路線番号を取得する必要があります。
その路線番号データは、ここで取得できるらしい。
swiftでそれを取得するコードは以下。

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.

        rosenCount = 0//路線数取得のための変数
        
        //URLオブジェクトの作成
        let url = NSURL(string: "http://tutujibus.com/rosenidLookup.php")//03路線番号データ
        
        //通信を行うオブジェクト作成
        let urlSession = NSURLSession.sharedSession()
        
        //データをダウンロードするタスクの作成(データ読み込みが終わったらonFinishを実行)
        let task = urlSession.dataTaskWithURL(url!, completionHandler: onFinish)
        
        //タスク実行
        task.resume()
    }

    //データダウンロード完了後に実行
    func onFinish(data: NSData?, res: NSURLResponse?, error: NSError?){
        if let nsstr = NSString(data: data!, encoding: NSUTF8StringEncoding){
            let str = String(nsstr)
            print(str)
            
            let arr2 = str.componentsSeparatedByString("\"id\":")

            
                //arr2[i].substringFromIndex()
            for var i=0; i<arr2.count; i++ {
                print(arr2[i])
                for var j=0; j<100 ; j++ {
                    if (arr2[i].rangeOfString("\(j)") != nil) && j >= i  {
                        print(j)
                        rosenId.append(j)
                        rosenCount++
                    }
                }
            }
            
            for var k = 0; k<rosenCount; k++ {
                print(rosenId[k])
            }
            print(rosenId.count)
        }
    }

これをやると、以下のようにデバッグエリアに表示されます。

({"rosen":[{"id":"1","name":"中央線"},{"id":"2","name":"鯖江南線"},{"id":"3","name":"新横江線"},{"id":"4","name":"神明線"},{"id":"5","name":"片上・北中山線"},{"id":"6","name":"立待線"},{"id":"7","name":"吉川線"},{"id":"8","name":"豊線"},{"id":"9","name":"中河・北中山線"},{"id":"10","name":"河和田線"},{"id":"11","name":"丹南高校線"},{"id":"12","name":"歴史の道線"},{"id":"99","name":"福鉄鯖浦線"}]})
({"rosen":[{
"1","name":"中央線"},{
1
"2","name":"鯖江南線"},{
2
"3","name":"新横江線"},{
3
"4","name":"神明線"},{
4
"5","name":"片上・北中山線"},{
5
"6","name":"立待線"},{
6
"7","name":"吉川線"},{
7
"8","name":"豊線"},{
8
"9","name":"中河・北中山線"},{
9
"10","name":"河和田線"},{
10
"11","name":"丹南高校線"},{
11
"12","name":"歴史の道線"},{
12
"99","name":"福鉄鯖浦線"}]})
99
1
2
3
4
5
6
7
8
9
10
11
12
99
13

まあいろいろ試行錯誤経過をそのまま残してるので無駄が多いですが、何をしてるかというと、まず路線番号データがどうやって格納されているか。
それによれば、"id":の後に"1"のように数字で格納されているらしい。
まあ目視で、1から12までと、いきなり99に飛ぶ、という全13の路線がある。
13個なので、この後バス停データを取るために、

"http://tutujibus.com/busstopLookup.php?rosenid=1"

というURLを13個(最後のrosenid=の後に数字に、取得された路線番号idを入れればいいだけ)て入力すればいいのですが、それだと格好悪いかもしれないので、一応、取得された路線番号データからidの数字だけを抜き出すコードも書いておきました。

let arr2 = str.componentsSeparatedByString("\"id\":")
というところで、"id:"が含まれた部分で文字列を分断して、分断された文字列をarr2という配列に順次格納しています。

そうすると、各配列には、
"1","name":"中央線"
"2","name":"鯖江南線"
のような、"路線番号"から始まる文字列が入っていく。
この路線番号データを数字として取得したいので、この中から数字を取り出すために、

for var i=0; i<arr2.count; i++ {
                print(arr2[i])
                for var j=0; j<100 ; j++ {
                    if (arr2[i].rangeOfString("\(j)") != nil) && j >= i  {
                        print(j)
                        rosenId.append(j)
                        rosenCount++
                    }
                }
            }

を書いているのですが、何をしているかというと、各配列ごとに、jという整数を路線番号の最大値(99)まで和算して、jに等しくなる整数が含まれた文字列があれば、そのjの値をrosenIDという配列に追加していく。
これで、rosenIDには、路線番号の整数データだけが格納されていくと。
いろいろ面倒くさいですが。

全路線のバス停の表示

鯖江市コミュニティバスつつじバス
今度は全路線のバス停を表示してみる。

01.路線毎のバス停の座標データによれば、バス停座標を取得するには、そのバス停が所属している路線番号を指定したURLでクエリをかける必要がある。
http://tutujibus.com/busstopLookup.php?rosenid=1(1は路線番号)
のように。

とりあえず路線番号データはすでに取得できていて、rosenId[]という配列に格納してあるので、それを利用して次のように指定してあげれば良い。

for var rosenCount = 0; rosenCount<rosenId.count; rosenCount++ {
                //print(rosenId[rosenCount])
                let url2 = NSURL(string: "http://tutujibus.com/busstopLookup.php?rosenid=\(rosenId[rosenCount])")//01路線毎のバス停の座標データ
                let urlSession = NSURLSession.sharedSession()
                let task2 = urlSession.dataTaskWithURL(url2!, completionHandler: onFinish2)
                task2.resume()
            }

rosenCountという変数を作って、配列の引数としてその変数を設定し、rosenId.countで路線番号数だけカウントアップしながら"http://tutujibus.com/busstopLookup.php?rosenid=\(rosenId[rosenCount])"でURLを作ってやれば、全部の路線番号分だけバス停データが取得できる。
なお、NSDATAを取得した後に実行されるメソッドはonFinish2に書いてあるので、そのonFinish2を次のように書いてみました。

func onFinish2(data: NSData?, res: NSURLResponse?, error: NSError?){
        if let nsstr = NSString(data: data!, encoding: NSUTF8StringEncoding){
            let str = String(nsstr)
            //print(str)
            
            //バス停番号
            var id = str.componentsSeparatedByString("\"id\":")
            for var i=1; i<id.count; i++ {
                if let point = id[i].rangeOfString(",") {
                    //","から前を抜き出す
                    id[i] = id[i].substringToIndex(point.endIndex)
                    //","より前を抜き出す
                    id[i] = id[i].substringToIndex(id[i].endIndex.advancedBy(-1))
                    print(id[i])
                }
            }
            
            //バス停名前
            var name = str.componentsSeparatedByString("\"name\":")
            for var i=1; i<name.count; i++ {
                if let point = name[i].rangeOfString(",") {
                    //","から前を抜き出す
                    name[i] = name[i].substringToIndex(point.endIndex)
                    //","より前を抜き出す
                    name[i] = name[i].substringToIndex(name[i].endIndex.advancedBy(-1))
                    print(name[i])
                }
            }
            
            //バス停緯度
            var lat = str.componentsSeparatedByString("\"latitude\":")
            for var i=1; i<lat.count; i++ {
                if let point = lat[i].rangeOfString(",") {
                    //","から前を抜き出す
                    lat[i] = lat[i].substringToIndex(point.endIndex)
                    //","より前を抜き出す
                    lat[i] = lat[i].substringToIndex(lat[i].endIndex.advancedBy(-1))
                    print(lat[i])
                }
            }
            
            //バス停経度
            var lon = str.componentsSeparatedByString("\"longitude\":")
            for var i=1; i<lon.count; i++ {
                if let point = lon[i].rangeOfString("}") {
                    //"}"から前を抜き出す
                    lon[i] = lon[i].substringToIndex(point.endIndex)
                    //"}"より前を抜き出す
                    lon[i] = lon[i].substringToIndex(lon[i].endIndex.advancedBy(-1))
                    print(lon[i])
                }
            }
            
            print(name.count)
            print(lat.count)
            print(lon.count)
            
            for var i=1; i<name.count; i++ {
                //緯度経度から地図上の座標を取得
                let location = CLLocationCoordinate2DMake(CLLocationDegrees(lat[i])!,CLLocationDegrees(lon[i])!)
                //CLLocationCoordinate2Dに変換
                let mapPoint:CLLocationCoordinate2D = location
                
                //ピン生成
                let annotation = MKPointAnnotation()
                //ピンの場所
                annotation.coordinate  = mapPoint
                //ピンのタイトル
                annotation.title       = "\(id[i])\(name[i])"
                //ピンのサブタイトル
                annotation.subtitle    = "\(name[i])"
                //ピンを地図上に追加
                self.mapView.addAnnotation(annotation)
            }
        }


このページによれば、レスポンス形式が以下のようになってるみたいです。

◆レスポンス形式のサンプル
jsonp_callback({"busstop":[{"id":"1","name":"JR鯖江駅(1番のりば)","latitude":35.943302,"longitude":136.188187},{"id":"2","name":"本町1丁目(東)","latitude":35.944408,"longitude":136.185257},・・・]})
id バス停番号
name バス停名
latitude バス停緯度
longitude バス停経度

中カッコとかカンマとか、欲しくない余計なデータが入っているので、そういうのを削除しながら配列に入れていく。
例えば緯度を撮りたい場合。

"latitude:"という文字列の直後に緯度の数値が入っているので、
var lat = str.componentsSeparatedByString("\"latitude\":")
で"latitude:"が現れるごとにもとデータを分割してlat[]という配列に順次入れていく。
そうすると、lat[]の要素1以上では、すべて緯度の数値データから始まる文字列になっている。
ただ、緯度の数字データに続けて、,"longitude":136.18818・・・みたいな余計な文字列が入っているのでそれを削除したい。
よく見れば、その余計な文字列は必ずカンマに続けて始まっているので、カンマ","以降はすべて削除するために
let point = lat[i].rangeOfString(",")
というインデックスpointを作っておいて、
lat[i] = lat[i].substringToIndex(point.endIndex)
を書くと、lat[i]は35.943302という数値とカンマ","で構成された配列になる。
さらにこのカンマが邪魔なので、
lat[i] = lat[i].substringToIndex(lat[i].endIndex.advancedBy(-1)
で最後のカンマだけ削除して出来上がり。

あとは、以下のようにピンを立ててやれば完成かと。

for var i=1; i<name.count; i++ {
                //緯度経度から地図上の座標を取得
                let location = CLLocationCoordinate2DMake(CLLocationDegrees(lat[i])!,CLLocationDegrees(lon[i])!)
                //CLLocationCoordinate2Dに変換
                let mapPoint:CLLocationCoordinate2D = location
                
                //ピン生成
                let annotation = MKPointAnnotation()
                //ピンの場所
                annotation.coordinate  = mapPoint
                //ピンのタイトル
                annotation.title       = "\(id[i])\(name[i])"
                //ピンのサブタイトル
                annotation.subtitle    = "\(name[i])"
                //ピンを地図上に追加
                self.mapView.addAnnotation(annotation)
            }

ピンの画像をバス停にする

ただこれだと赤いピンが立てられるだけなので、バス停らしいデータが欲しい。
鯖江市ホームページには11.バス停アイコンデータも用意されているのでこれを利用。

以下のようなメソッドをビューコントローラークラスに書いておけば画像が変更されます。

func mapView(mapView: MKMapView!, viewForAnnotation annotation: MKAnnotation!) -> MKAnnotationView! {
        let identifier = "annotation"
        let annotationView = MKAnnotationView(annotation: annotation, reuseIdentifier: identifier)
        annotationView.image = UIImage(named: "busstop32") // busstop32は画像データの名前
        annotationView.canShowCallout = true
        annotationView.annotation = annotation
        return annotationView
        

これで上記画像のようにバス停が立てられます。

あとは同じようにバスのリアルタイム位置情報をタイマー関数で定期的に取得してやれば、所望のアプリができそうです。

定期的な間隔でピンを表示することができない

[Swift]鯖江市コミュニティバスつつじバスをマップ表示するアプリ

次はいよいよ目標を路線バスのリアルタイム表示です。
まあ基本的な考え方は同じで、ただ異なる点は、NSTimerで定期的(1秒おき)にサイトにデータを取りに行って表示すること、だけなのですが、実はこの単純なことなので表示の点では割と苦労しまして、無理やり解決した感がある。

これ、どの日本語サイトを見ても解決方法がなかったので、みんな苦労してないのだろうか。。。
ちな、こういう時は海外サイトを検索するのだけど、英語での検索式が思い浮かばなかったので詰んだ。
まあ日本語でも最適な言い回しがわからなかったから検索できなかったのだけれども。。。

何が言いたいのかというと、自分なりの言い方で表現すると、
「self.mapView.addAnnotation(annotation)」
でmapViewにピン(アノテーション)を追加するプログラムはそのままでは表示されず、mapViewをタッチ操作して何らかのジェスチャーを与えてmapViewが更新されると、更新後にアノテーションが表示される、
ということです。

よくわからないかもしれませんが、例えば1秒おきにバスを表示しようとしても、そのまま静観しているだけではピンはぜんぜん表示されない。
で、例えばアプリ開始10秒後にマップを指で触って拡大したり表示領域を移動させたり、みたいな操作を行うと、それまで蓄積されていた10個分のピンがまとめて表示される。

これ不具合じゃないのか。。。
個人的には、画面をずっと見ているだけで路線バスがリアルタイムに動く様子が地図上で観察できる、というアプリを作りたかったので、1秒おきにユーザが操作をし続けないとリアルタイムにピンが表示されないのでは困る。

いろいろいじってみたところ、どうやらmapViewの表示(mapView.setRegion(centerPosition,animated:true))と、アノテーションの表示(self.mapView.addAnnotation(annotation))がほぼ同時に行われないと、ピンが表示されないのでは、と推察していますが、本質のところはわからない。

路線バス表示のコード

とりあえず、路線バスの座標データの取得と表示を行うコードは以下のようにしてみました。
いろいろ試行錯誤しながらだったので無駄な処理もありますが、面倒なので直してないです。

なお、バスデータは05.号車を指定したバスの位置データから取得しています。
すごいたくさんあるのかと思っていろいろ調べてみたら、8号車までしか実はないんじゃないかと途中で気づいて、途中で変数xが1から7まで足し合わされているのはそういうことです。

あと、xが7になった時、つまり最後の7号車の位置座標を取得したあとに「gamenFlg」という変数に応じて、mapViewを0.1だけ1秒おきに左に動かしたり右に動かしたりしてる処理。
これは上記の不具合対策です。
自分で操作せずにプログラム内でマップビューを動かせば、ピンもリアルタイムに表示されてくれるんじゃないかと思って入れました。そうしたらちゃんと表示されるようになった。
根本的な対策にはなってませんが、一応。
あと、busAnnotationはバスのアノテーションを表示するための配列。一応要素数10くらい用意して全部の要素にMPPointAnnotation()を入れてイニシャライズしてます。

あとは、、、書くのが面倒になってきた。。。


class ViewController: UIViewController, MKMapViewDelegate, CLLocationManagerDelegate {

    var busAnnotation:[MKPointAnnotation] = [MKPointAnnotation]()
  //・略

override func viewDidLoad() {
        super.viewDidLoad()

//・略

        for var c=0; c<10; c++ {
            let mkPointAnnotation = MKPointAnnotation()
            busAnnotation.append(mkPointAnnotation)
        }
}
timer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: "busReal", userInfo: nil, repeats: true)

    func busReal() {
        for var x = 1; x<8; x++ {
            let url3 = NSURL(string: "http://tutujibus.com/busLookup.php?busid=\(x)")//05.号車を指定したバスの位置データ
            let urlSession = NSURLSession.sharedSession()
            let task3 = urlSession.dataTaskWithURL(url3!, completionHandler: onFinish3)
            task3.resume()
            if x == 7 {
                if gamenFlg == 0{
                    var cr: MKCoordinateRegion = mapView.region
                    cr.center.latitude = cr.center.latitude - 0.000001
                    //mapView.region.center.latitude = mapView.region.center.latitude-0.1
                    mapView.setRegion(cr, animated: false)
                }else{
                    var cr: MKCoordinateRegion = mapView.region
                    cr.center.latitude = cr.center.latitude + 0.000001
                    //mapView.region.center.latitude = mapView.region.center.latitude-0.1
                    mapView.setRegion(cr, animated: false)
                }
                
            }
        }
    }

    func onFinish3(data: NSData?, res: NSURLResponse?, error: NSError?){
        
        if data != nil {
            if let nsstr = NSString(data: data!, encoding: NSUTF8StringEncoding){
                let str = String(nsstr)
                //print(str)
                if let strTrue = str.rangeOfString("\"isRunning\":true"){
                    print(str)
                }
                
                
                //バスid
                var busid = str.componentsSeparatedByString("\"busid\":")
                for var i=1; i<busid.count; i++ {
                    if let point = busid[i].rangeOfString(",") {
                        //","から前を抜き出す
                        busid[i] = busid[i].substringToIndex(point.endIndex)
                        //","より前を抜き出す
                        busid[i] = busid[i].substringToIndex(busid[i].endIndex.advancedBy(-1))
                        print(busid[i])
                    }
                }
                
                
                //バス緯度
                var lat = str.componentsSeparatedByString("\"latitude\":")
                for var i=1; i<lat.count; i++ {
                    if let point = lat[i].rangeOfString(",") {
                        //","から前を抜き出す
                        lat[i] = lat[i].substringToIndex(point.endIndex)
                        //","より前を抜き出す
                        lat[i] = lat[i].substringToIndex(lat[i].endIndex.advancedBy(-1))
                        print(lat[i])
                    }
                }
                
                //バス経度
                var lon = str.componentsSeparatedByString("\"longitude\":")
                for var i=1; i<lon.count; i++ {
                    if let point = lon[i].rangeOfString(",") {
                        //"}"から前を抜き出す
                        lon[i] = lon[i].substringToIndex(point.endIndex)
                        //"}"より前を抜き出す
                        lon[i] = lon[i].substringToIndex(lon[i].endIndex.advancedBy(-1))
                        print(lon[i])
                    }
                }
                
                
                
                for var i=1; i<busid.count; i++ {
                    if shokai == 1 {
                        mapView.removeAnnotation(busAnnotation[i])
                    }
                    shokai=1
                    
                    pinFlg = 0
                    //緯度経度から地図上の座標を取得
                    let location = CLLocationCoordinate2DMake(CLLocationDegrees(lat[i])!,CLLocationDegrees(lon[i])!)
                    //CLLocationCoordinate2Dに変換
                    let mapPoint:CLLocationCoordinate2D = location
                    
                    //ピンの場所
                    busAnnotation[i].coordinate  = mapPoint
                    //ピンのタイトル
                    busAnnotation[i].title       = "\(busid[i])"
                    //ピンのサブタイトル
                    busAnnotation[i].subtitle    = "バス"
                    //ピンを地図上に追加
                    self.mapView.addAnnotation(busAnnotation[i])

                }    
            }
        }
    }

アノテーション種別に応じて画像を変える

バスとバス停とを同時に地図表示するにあたり、各アイコンを変えたほうがいいと思うので、そういう時はデリゲートをいじってやるといいと思います。
ピンごとにタグをつけられたらいいなあと思ったりしてるのですが、そういうやり方がよくわからないので、とりあえず、バス停のピンにはサブタイトルで「バス停」とつけておいて、バスには「バス」とつける。
で、ピン表示する際に、サブタイトルに応じてバス停画像をつけるかバス画像にするかを判定してやれば、とりあえず見た目の画像はうまくいきますね。

    func mapView(mapView: MKMapView!, viewForAnnotation annotation: MKAnnotation!) -> MKAnnotationView! {
        let annotationView = MKAnnotationView(annotation: annotation, reuseIdentifier: identifier)
        if annotation.subtitle! == "バス停" {
            annotationView.image = UIImage(named: "busstop32") // バス停画像
            annotationView.canShowCallout = true
            annotationView.annotation = annotation
            
            
        }else if annotation.subtitle! == "バス" {
            annotationView.image = UIImage(named: "bus1") // バス画像
            annotationView.canShowCallout = true
            annotationView.annotation = annotation
            
        }
        return annotationView
    }

さて、さらにこれを発展させて、またクソゲーにも発展できそうです。

アップルストアに申請

ぼーっと見てるだけでバスや電車が動いてくアプリ

ぼーっと見てるだけでバスや電車が動いてくアプリ

というわけで、長々書いてきたけど、ぼーっと見てるだけでバスや電車が動いてくアプリをiphone向けに作ったのでアップルストアに申請してみた。
(なお、つつじバスロケーションWEB APIバスアイコン路線毎のバス停の座標号車を指定したバスの位置バス停アイコン)を使用しています。)

しかし個人的にはあまり満足いく出来になってないのだけど。

Swiftの場合、

timer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: "メソッド名", userInfo: nil, repeats: true)

で例えば1秒おきに所定サイトにJsonデータ読みに行ってそこに記載の緯度経度の位置情報に基づいたアノテーションを表示、というのを繰り返せばあっさりできるかと思っていたのだけど、
MapKitでアノテーションをリアルタイムに表示していく中で、かなり色々と苦労したので幾つかをメモ。

アノテーションの表示処理はバックグラウンドスレッドで

何を言ってるのか自分でもよくわからないけど、なんか1秒おきにマップにバスを表示しようと思って例えばこんな処理を書いてみたとする。

//緯度経度から地図上の座標を取得
let location = CLLocationCoordinate2DMake(CLLocationDegrees(lat[1])!,CLLocationDegrees(lon[1])!)
//CLLocationCoordinate2Dに変換
let mapPoint:CLLocationCoordinate2D = location
                        
//ピン生成
self.busAnnotation = MKPointAnnotation()
//ピンの場所
self.busAnnotation.coordinate  = mapPoint
//ピンのタイトル
self.busAnnotation.title       = "\(busid[1])"
print(busid[1])
//ピンのサブタイトル
self.busAnnotation.subtitle    = "バス"
//ピンを地図上に追加
self.mapView.addAnnotation(self.busAnnotation)
                    
                        
self.statusLabel.text = "\(busid[1])号車の現在位置とその関係するバス停を表示しています。\rBus Number: \(self.bangou) and the relevant busstops."

で、なぜかこれがうまい具合に表示されない。表示させようと思うと、自力で画面をジェスチャー動作で動かすなどすると、なぜかそれまでに蓄積されてきたアノテーションが一気に表示されたりする。
だったら、プログラムで自動的に1秒おきに、画面を目視では確認できないくらいに微妙に左右に動かすような処理を入れてやればリアルタイムに表示されていくのではないかと思ってそうやったら、確かにできたのだけど、なんか途中でアプリが落ちたりする。
デバッグエリアのところに、「バックグラウンドスレッドとメインスレッドでの処理がどうたらこうたら」みたいなエラーが出ていて(具体的にどんなエラーメッセージだったか忘れたけど)、そんな感じでいろいろググってたら、どうやらどうもアノテーションの表示に関する上記処理、特に
//ピンを地図上に追加
self.mapView.addAnnotation(self.busAnnotation)
あたりは、バックグラウンドスレッドなるもので処理しておいたほうがいいらしい。
全然意味わかんないけど。

例えば、MKMapView add annotations in background threadとかに、
dispatch_async(dispatch_get_main_queue(),{}),
のクロージャの中に上記の地図上にピンを置く操作をしないとまずいような気がする記述があった。

だからそうしたら割と上手く行った。

でも、いろいろアプリを使っていると急に途中で落ちたりする時があって、その原因がわからなくて悶々としたし、
特定のピンを削除する方法も難しい。
いや、例えば
self.mapView.removeAnnotation(self.mapView.annotations.last!)
なんかを使えば、前回おいたピンを削除できるのだけど、だから今回のバスの位置を置く前にこの処理をかけば、前回の古いバスの画像が削除されて新しいバスの画像が表示されるから、リアルタイムにバスが動いてるのがマップ上で楽しめるなあと単純に考えたのだけど、どうもうまくいかなくて、結局削除する処理を入れずに、安全パイでリアルタイムにどんどんバスの画像が増えていくだけのアプリにしてしまった。
(上記最初の画像にて、1号車のバスのアイコンがたくさんあるのはそういう理由)

福井県鯖江市のつつじバスの現在位置がわかるアプリ「つつじなう」をリリースしました。

つつじなうアプリ

今までいろいろ書いてきた路線バスの現在位置を表示するアプリについて、一応「つつじなう」というタイトルで申請していたのがリリースされました。
これでリリースアプリ数は以下のようになります。
iPhone/iPad: 39
Android: 2

つつじなう(App Store)

以下、説明です。

福井県鯖江市のつつじバスのリアルタイムな場所、およびバス停をマップ上で視認できるアプリです。
1号車から7号車のいずれかのボタンを押すと、その号車の現在位置が1秒おきにマッピング追加されていきます。
また、その号車の関係するバス停も表示されます。
さらに、自分の現在位置もマップ表示されているので、そのバスとの相対的な距離がつかめます。
営業時間外のバスについては、ボタンを押しても、バスおよび関連するバス停は表示されません。

なお、以下の情報を使用しています。
つつじバスロケーションWEB API
http://www.city.sabae.fukui.jp/users/tutujibus/web-api/web-api.html

01.路線毎のバス停の座標データ
http://www.city.sabae.fukui.jp/users/tutujibus/web-api/01.html

05.号車を指定したバスの位置データ
http://www.city.sabae.fukui.jp/users/tutujibus/web-api/05.html

10.バスアイコンデータ
http://www.city.sabae.fukui.jp/users/tutujibus/web-api/10.html

11.バス停アイコンデータ
http://www.city.sabae.fukui.jp/users/tutujibus/web-api/11.html

本当は、ただぼーっと眺めてるだけで、バスの1号車から7号車までがちょっとずつ生き物みたいに動いてる様子が楽しめるみたいなアプリを想定していたのですが、
バスの現在位置の全データを定期的に取得してきて表示する、という処理が思った以上に重いみたいで、よくわからないエラーで落ちる。
あと、全バスデータ分の全バス停を一気に表示するとごちゃごちゃしてて見づらい。

そういう理由から、バス1号車から7号車までのデータを分割して、各々に対応するボタンを押したらその号車分だけのバスおよびバス停が表示されるようにした。

メモリ管理とか処理速度とか重要

いや当たり前だろとか思われそうですが。
しかし、iPhoneとかiPadでものすごい優れもので、今まで自分が作ってきたアプリで特に処理スピードとか処理の重さとか気にする必要がなかったのですよ。
例えばものすごい数のオブジェクトを一気に画面表示させて一個一個動かしたりとかしても画面が鈍るようなことなかったですし、すごいCPU積んでるんだなあとか感心してましたし。
しかしながら今回、多くのデータを一気に取得しに行って一気に表示する、みたいのを繰り返してるとそのうちアプリが落ちるという現象に出くわして、もっとそういう基本的なことを気にした方がいいのだなと思った。

特にMapkitを扱う場合にはそれが顕著なような気がした。
さらにアノテーション表示するときにはバックグラウンドスレッドで処理させないとうまくいかなくなるとか、いろいろ作っていく上で初めて知ったこともあるし、まあ苦労した分ためにはなったと思う。

で、今後はこういった位置情報を利用してクソゲーを作ってみたいと考えていたのですが、
またいろいろバックグラウンド処理とか考えるのが面倒臭くなってきたし、面倒くさいことが苦手なので、やっぱりやめようかなとか思った。
何かにつけてこういう怠け癖があるところが自分のダメなところなのですが、怠けた方が人生楽なので、楽したいです。

 - アプリ開発