NCMBでは公式SDKとしてSwift/Objective-C/Kotlin/Java/Unity/JavaScript SDKを用意しています。また、それ以外にもコミュニティSDKとして、非公式ながらFlutter/React Native/Google Apps Script/C#/Ruby/Python/PHPなど幅広い言語向けにSDKが開発されています。
今回は公式SDKの一つ、Kotlin SDKを使って地図検索アプリを作ってみます。前回は画面の仕様とSDKの初期化について解説しました。今回は位置情報をデータストアにインポートする流れを解説します。
完成版のコード
作成したデモアプリのコードはNCMBMania/kotlin-map-searchにアップロードしてあります。
インポート画面について
インポート画面はMapBottomNavigationのタブで読み込まれています。 ImportScreen がそうです。
NavHost(navController = navController, startDestination = "Map") {
composable("Map") { MapScreen()}
composable("Import") { ImportScreen()}
}
インポート画面の構築
まず ImportScreen では文字列の配列が入る logs を mutableStateListOf で用意します。
@Composable
fun ImportScreen() {
// ログメッセージ用
var logs = remember { mutableStateListOf<String>() }
UIは次のようになります。インポート実行タンをタップすると、既存データの削除と駅情報を追加します。
Column(
modifier = Modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally
) {
Spacer(Modifier.size(20.dp))
Button(
onClick = {
// 既存データの削除
deleteAllStations()
// JSONに入っている駅情報をNCMBのデータストアに保存
for (i in 0 until stationsJson.length()) {
val str = saveStation(stationsJson.getJSONObject(i))
logs.add(str)
}
}
){
Text(text = "山手線データをインポートする")
}
LazyColumn(
) {
items(logs) { str ->
Text(text = str)
}
}
}
データのインポートについて
データをインポートする際には繰り返し処理できるように、まず既存データを削除しています。もし既存データがなかった場合でも(データストアのクラスがなくとも)エラーにはならず、空の配列が返ってくるだけです。
// 既存データの削除
deleteAllStations()
// JSONに入っている駅情報をNCMBのデータストアに保存
for (i in 0 until stationsJson.length()) {
val str = saveAllStations(stationsJson.getJSONObject(i))
logs.add(str)
}
deleteAllStations は以下のようになります。NCMBQueryを使ってデータを取得し、各データ(NCMBObject)のdeleteInBackgroundメソッドを使ってデータを削除します。 delete もありますが、同期処理なので時間がかかってしまいます。
// 既存のデータを削除する処理
fun deleteAllStations() {
// 検索対象のNCMBのデータストアクラス
val query = NCMBQuery.forObject("Station")
query.limit = 1000 // マックスまで指定しておく
// 検索は同期で
val stations = query.find()
// 削除は非同期で終わらせる
stations.forEach{
it.deleteInBackground(NCMBCallback { e, _ ->
})
}
}
データを削除したら、山手線の各駅の位置情報が入ったJSONファイルの内容をデータストアに反映します。
// JSONに入っている駅情報をNCMBのデータストアに保存
for (i in 0 until stationsJson.length()) {
val str = saveStation(stationsJson.getJSONObject(i))
logs.add(str)
}
この時利用しているJSONは下記のようになっています。
struct Station {
var name: String
var latitude: Double
var longitude: Double
}
読み込んだデータをデータストアに登録する registerDataStore 関数は以下のようになります。JSONファイルのパラメータを渡して、Stationクラスにデータを作成します。NCMBGeoPointを使って位置情報を保存するのがポイントです。こちらも保存を非同期にして高速化させています。
// 駅情報を保存する処理
fun saveStation(json: JSONObject ): String {
// 位置情報からNCMBGeoPointを作成
val geo = NCMBGeoPoint(latitude = json.getDouble("latitude"), longitude = json.getDouble("longitude"))
// データストアのインスタンスを作成
val station = NCMBObject("Station")
// 駅名と位置情報をセット
station.put("geo", geo)
station.put("name", json.getString("name"))
// 保存(量が多いので非同期で)
station.saveInBackground(NCMBCallback {e, _ ->
})
return "${station.getString("name")}を取り込みました"
}
全体のコード
ImportScreenの内容は次のようになります。
@Composable
fun ImportScreen() {
// ログメッセージ用
var logs = remember { mutableStateListOf<String>() }
// 山手線のJSONデータを読み込む処理
val context = LocalContext.current
var inputStream = context.assets.open("yamanote.json")
val json = BufferedReader(InputStreamReader(inputStream)).readText()
val stationsJson = JSONArray(json)
Column(
modifier = Modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally
) {
Spacer(Modifier.size(20.dp))
Button(
onClick = {
// 既存データの削除
deleteAllStations()
// JSONに入っている駅情報をNCMBのデータストアに保存
for (i in 0 until stationsJson.length()) {
val str = saveStation(stationsJson.getJSONObject(i))
logs.add(str)
}
}
){
Text(text = "山手線データをインポートする")
}
LazyColumn(
) {
items(logs) { str ->
Text(text = str)
}
}
}
}
// 既存のデータを削除する処理
fun deleteAllStations() {
// 検索対象のNCMBのデータストアクラス
val query = NCMBQuery.forObject("Station")
query.limit = 1000 // マックスまで指定しておく
// 検索は同期で
val stations = query.find()
// 削除は非同期で終わらせる
stations.forEach{
it.deleteInBackground(NCMBCallback { e, _ ->
})
}
}
// 駅情報を保存する処理
fun saveStation(json: JSONObject ): String {
// 位置情報からNCMBGeoPointを作成
val geo = NCMBGeoPoint(latitude = json.getDouble("latitude"), longitude = json.getDouble("longitude"))
// データストアのインスタンスを作成
val station = NCMBObject("Station")
// 駅名と位置情報をセット
station.put("geo", geo)
station.put("name", json.getString("name"))
// 保存(量が多いので非同期で)
station.saveInBackground(NCMBCallback {e, _ ->
})
return "${station.getString("name")}を取り込みました"
}
まとめ
今回はJSONファイルをインポートし、それを使ってNCMBObjectを作成しました。次回は地図コンポーネントと合わせて、位置情報検索を実装します。