【作成メモ】Kotlinで逆ジオコーディング利用時の注事項

Swiftだと特に問題なく街名を取得できていたのですが、kotlinでは躓いたのでメモをしておきます。

■逆ジオコーディングの基本的な流れ
①現在地の経度緯度を取得(これは問題なし)
②取得した経度緯度から住所を取得 ←問題アリ

※備考
「geocoder.getFromLocation」は非奨励になっており、「geocoder.getAddress」で逆ジオコーディングした。

■問題点
「geocoder.getAddress」で取得した住所でなぜか第二町名がnullになっている

具体的には以下がgeocoder.getAddressで取得したAdressのログ(住所は一部編集)です。
addressLinesでは「中野区中野7丁目」と取得できているが、
個別だとなぜか「locality=中野区」の次が「thoroughfare=7丁目」となっている。
Swiftの時は「sublocality」というプロパティがありますが、どうやらなさそう・・・??
たぶんバグなんでしょう。。

Address[addressLines=[0:"日本、〒164-0003 東京都中野区中野7丁目 建物サンプル"],feature=建物サンプル,admin=東京都,sub-admin=null,locality=中野区,thoroughfare=7丁目,postalCode=164-0003,countryCode=JP,countryName=日本,hasLatitude=true,latitude=35.7086545,hasLongitude=true,longitude=139.6836509,phone=null,url=null,extras=null]


■改善案
上記のとおり第一町名と番地は取得できるようなので、
以下の条件で住所の文字列(addressLines)から第二町名を取得することにした。
(そのうちSunLocalityが取得できるかもなので、その可能性を残した)

  • subLocality が空で、thoroughfarelocality が空でない場合は、住所の文字列から localitythoroughfare の間の文字列を抽出。
  • thoroughfare が空で、locality が空でない場合は、locality の後の文字列を抽出。
  • thoroughfare が空で、subLocality も空の場合は、locality を文字列とする。
  • thoroughfaresubLocalitylocality が共に空の場合は、代替の町名「こだま街」を使用。
  • 文字列は4文字までとし、4文字以上の場合は頭の4文字のみとする。
  • 文字列の途中に漢数字が入っている場合はその漢数字の直前までを文字列とする。

以下がそのソースです。

    private fun generateSecondChomeName(addressString: String, thoroughfare: String, locality: String, subLocality: String): String {
        var secondChomeName = ""

        // subLocality が空で、thoroughfare と locality が空でない場合は、住所の文字列から locality と thoroughfare の間の文字列を抽出
        if (subLocality.isEmpty() && thoroughfare.isNotEmpty() && locality.isNotEmpty()) {
            val startIndex = addressString.indexOf(locality) + locality.length
            val endIndex = addressString.indexOf(thoroughfare)
            if (startIndex >= 0 && endIndex > startIndex) {
                secondChomeName = addressString.substring(startIndex, endIndex)
            }
        }
        // thoroughfare が空で、locality が空でない場合は、locality の後の文字列を抽出
        else if (thoroughfare.isEmpty() && locality.isNotEmpty()) {
            secondChomeName = locality.substring(4.coerceAtMost(locality.length))
        }
        // thoroughfare が空で、subLocality も空の場合は、locality を文字列とする
        else if (thoroughfare.isEmpty() && subLocality.isEmpty() && locality.isNotEmpty()) {
            secondChomeName = locality
        }
        // thoroughfare が空で、subLocality 、 locality が共に空の場合は、代替の町名「こだま街」を使用
        else if (thoroughfare.isEmpty() && subLocality.isEmpty() && locality.isEmpty()) {
            secondChomeName = "こだま街"
        } else {

            // locality が空でなければ追加
            if (locality.isNotEmpty()) {
                secondChomeName = locality
            }

            // subLocality が空でなければ追加
            if (subLocality.isNotEmpty()) {
                secondChomeName = subLocality
            }
        }

        // 文字列が4文字以上の場合は、最初の4文字まで取得
        if (secondChomeName.length > 4) {
            secondChomeName = secondChomeName.substring(0, 4)
        }

        // 文字列中に漢数字がある場合、その直前までを抽出
        val regex = Regex("[一二三四五六七八九十百千万億兆]+")
        val matchResult = regex.find(secondChomeName)
        if (matchResult != null) {
            secondChomeName = secondChomeName.substring(0, matchResult.range.start)
        }

        // 英数字の場合は "こだま街" に置き換える
        if (secondChomeName.matches(Regex("[a-zA-Z0-9]+"))) {
            secondChomeName = "こだま街"
        }

        return secondChomeName
    }

今回は用途として町名を4文字以上使わないのでその辺のコードは不要かも。
そのうちsubLocalityを取得できるようになることを信じて、subLocality.isNotEmpty()を最後に記載。

以上です。
まあ町名を抽出して使うという稀有なケースはなかなかありませんが、
このページに辿り着いた方の参考になれば幸いです。