地図データを空間検索してチャートで表現する 〜Charts 連携〜

Document created by yuki_ozawaesrij-esridist Employee on Aug 1, 2017Last modified by masanobu_hiranoesrij-esridist on Aug 8, 2017
Version 4Show Document
  • View in full screen mode

今回は、簡単にパイチャートやレーダーチャートなどの様々なチャートを作成することができる iOS のチャート表示用ライブラリ「Charts」と連携して、地図上に表示しているデータの属性をチャートで表現する方法をご紹介します。
Charts: https://github.com/danielgindi/Charts


Charts は軸項目のラベルや数値の変数を API に渡すことで iOS アプリ上にチャートを表示します。本記事では、地図上に表示している位置データの個数を Charts を使用してバーチャートで表示してみます。
地図表示には、地図アプリ開発キットの ArcGIS Runtime SDK for iOS(以下、iOS SDK)を使用します。iOS SDK にはチャートを表示する機能は提供されていないため、自作するか Charts のような専用のライブラリを利用して機能を作成する必要があります。

 

今回作成したアプリのイメージです。
アプリ は GitHub にも公開していますので、実際に操作してみてください。

 

 

地図表示/検索機能の実装

アプリで表示する地図上の位置データは、街なかの危険な箇所(架空のデモ データです)をその種別で色分け表示したものです。iOS SDK には位置データの空間検索機能が備わっており、地図上で特定の範囲を指定して、その範囲内にある該当するデータを検索できます。例えば、特定の市区町村や、地図上にフリーハンドで描画した範囲などを空間的な検索条件として使用できます。
今回作成したアプリでは、地図の操作をハンドリングして、アプリ上の地図の表示範囲が更新されたら、現在の表示範囲から動的に空間検索を行います。その検索結果(現在の表示範囲にある危険個所データの種別ごとの個数)を Chatrs の API に渡しバーチャートを作成しています。

 

以下は iOS SDK の地図表示・データ検索の処理のサンプル コード(Swift)です。

 

override func viewDidLoad() {

        super.viewDidLoad()

       

        // Web マップの ID を指定して、Web マップを作成

        // https://esrijapan.github.io/arcgis-dev-resources/create-webmap

        self.webMap = AGSWebMap(itemId: "fe0c760f0a404b37a8f7eb004c680c51", credential: nil)

        self.webMap.delegate = self

       

        // Web マップを開く

        self.webMap.open(into: self.mapView)

    }

   

 

    // MARK: - AGSWebMapDelegate

    func webMap(_ webMap: AGSWebMap!, didLoad layer: AGSLayer!) {

       

        // Web マップのレイヤーがロードされた際に、検索対象レイヤーをレイヤー名から判定し取得

        print(layer.name)

        if layer.name == "報告" {

           

            // Charts API に渡すデータ種別のラベルとシンボル色を取得し、配列に格納

            self.featureLayer = layer as! AGSFeatureLayer

            let renderer = self.featureLayer.renderer as! AGSUniqueValueRenderer

           

            for i in 0 ..< renderer.uniqueValues.count  {

                let uniqueValue = renderer.uniqueValues[i] as! AGSUniqueValue

                colors.append(uniqueValue.symbol.color)

                labels.append(uniqueValue.label)

            }

 

            // 検索するレイヤーの URL を指定してフィーチャ検索用タスク(AGSQueryTask)を作成

            self.queryTask = AGSQueryTask(url: self.featureLayer.url)

            self.queryTask.delegate = self

           

            //フィーチャ検索用のパラメータを設定

            self.query = AGSQuery()

            self.query.geometry = self.mapView.visibleArea() // 地図の現在の表示範囲を空間条件に設定

            self.query.spatialRelationship = .contains // 現在の表示範囲に「含まれる」データを検索

            self.query.outFields = ["type"]

            self.query.returnGeometry = false

            // 検索結果フィーチャの種別(「type」フィールドの値)ごとの項目数を返す

            self.query.groupByFieldsForStatistics = ["type"]

            let count = AGSOutStatistic()

            count.onStatisticField = "type";

            count.statisticType = .count

            count.outStatisticFieldName = "CountOfTypes"  // 各種別の個数を格納する任意のフィールド名

            self.query.outStatistics = [count]

           

            // パラメータをもとに空間検索を実行(アプリ起動時)

            self.queryTask.execute(with: self.query)

           

            // マップのズームとパンニングの監視用の Notification を作成

            NotificationCenter.default.addObserver(self, selector: #selector(self.excuteQuery(notification:)), name: NSNotification.Name.AGSMapViewDidEndPanning, object: nil)

            NotificationCenter.default.addObserver(self, selector: #selector(self.excuteQuery(notification:)), name: NSNotification.Name.AGSMapViewDidEndZooming, object: nil)

        }

    }

   

   

    func excuteQuery(notification: NSNotification?) {

        // マップのズームとパンニング時に、現在のマップの表示範囲をもとに空間検索を実行

        self.query.geometry = self.mapView.visibleArea()

        self.queryTask.execute(with: self.query)

    }

   

   

    // MARK: - AGSQueryTaskDelegate

    func queryTask(_ queryTask: AGSQueryTask!, operation op: Operation!, didFailWithError error: Error!) {

        print("空間検索のエラー:\(error.localizedDescription)")

    }

   

   

    func queryTask(_ queryTask: AGSQueryTask!, operation op: Operation!, didExecuteWithFeatureSetResult featureSet: AGSFeatureSet!) {

       

        // 空間検索の検索結果の処理

        // 各種別の個数を格納する配列をリセット

        self.typeCounts = []

        for _ in 0 ..< self.labels.count {

            self.typeCounts.append(0)

        }

       

        // 種別ごとの個数を配列に格納

        for i in 0 ..< featureSet.features.count {

            let graphic = featureSet.features[i] as! AGSGraphic

            if let typeNumber = graphic.attribute(forKey: "type") as? Int

            {

                let typeCount = graphic.attribute(forKey: "CountOfTypes") as! Int

                self.typeCounts[typeNumber] = Int(typeCount)

                print("種別:\(self.labels[typeNumber]), 個数:\(typeCount)")

            }

        }

       

        // チャートの作成

        self.createChart()

    }

 

 

Charts の使用

 

インストール

CocoaPods を使用してインストールします。

pod 'Charts'

 

チャートを表示するビューの作成

今回はバーチャートを表示するので、UIView を継承した BarChartView ビュークラスを作成し、Storyboard 上に追加します。

 

 

 

 

チャートの作成(チャートのデータの設定)

iOS SDK で空間検索した結果をもとにチャートを作成します。

 

func createChart() {

        // バーチャートの作成

        self.chartView.backgroundColor = UIColor.white.withAlphaComponent(0.5) // 背景を半透過

        self.chartView.chartDescription?.text = "" // 説明ラベルを非表示にする

 

        self.chartView.xAxis.valueFormatter = IndexAxisValueFormatter(values:self.labels) // X軸のラベルを設定

        self.chartView.xAxis.labelPosition = .bottomInside // ラベルの表示位置を設定

        self.chartView.xAxis.labelRotationAngle = 30.0 // ラベル表示の回転角度を設定

 

        // データの数値(Y軸の値)を設定

        var dataEntries: [BarChartDataEntry] = []

        for i in 0..<self.typeCounts.count {

            let dataEntry = BarChartDataEntry(x: Double(i), y: Double(self.typeCounts[i]))

            dataEntries.append(dataEntry)

        }

        let chartDataSet = BarChartDataSet(values: dataEntries, label: "報告件数")

       

        // 各バーの色を設定

        chartDataSet.colors = self.colors

        // バーチャートをビューに設定・表示

        self.chartView.data = BarChartData(dataSet: chartDataSet)

}

 

地図上で表示している位置データをチャートで表現する例をご紹介してきました。今回のサンプル アプリでは Charts を使用して簡単なバーチャートを表示しましたが、その他にもパイチャートやレーダーチャートなど多くのチャートの種類が用意されていて、チャートの細かな表現設定も行えます。

 

パイチャートで表現した例

 

 

 

また、iOS SDK の検索機能では、位置データの属性情報に数値が含まれている場合、検索結果のデータの平均や最大/最小値、標準偏差などを自動で計算して取得することもできるので、色々なチャートの表現が可能になります。

iOS SDK の開発・評価は、開発者向けクラウドサービス(ArcGIS for Developers)のアカウントを作成すれば無償で利用できますので、こちらもぜひお試しください。

 

※ ArcGIS Runtime SDK for iOS の現在のメジャー バージョンは 100.1 ですが、検索の統計機能が最新バージョンでは未実装(今後実装予定)のため、本記事のサンプル アプリは旧バージョンの 10.2.5 を使用して開発しています

 

関連リンク

1 person found this helpful

Attachments

    Outcomes