Home > OS >  Kotlin Sorting a list of objects based on their coordinate( lat and long) using Haversine formula
Kotlin Sorting a list of objects based on their coordinate( lat and long) using Haversine formula

Time:01-11

I want to sort a list based on their latitude and longitude... Here is my code:

import java.util.*
import com.google.gson.GsonBuilder
import java.io.File
import java.io.InputStream
import java.util.Comparator

data class Property(val Pcode: Int, val Locality: String, val State: String, val Comments: String, val Category: String, val Longitude: Double, val Latitude: Double)


class SortPlaces(currentLatitude: Double, currentLongitude: Double) : Comparator<Property> {
    var currentLat: Double
    var currentLng: Double
    override fun compare(property1: Property, property2: Property): Int {
        val lat1: Double = property1.Latitude
        val lon1: Double = property1.Longitude
        val lat2: Double = property2.Latitude
        val lon2: Double = property2.Longitude
        val distanceToPlace1 = distance(currentLat, currentLng, lat1, lon1)
        val distanceToPlace2 = distance(currentLat, currentLng, lat2, lon2)
        return (distanceToPlace1 - distanceToPlace2).toInt()
    }

    fun distance(fromLat: Double, fromLon: Double, toLat: Double, toLon: Double): Double {
        val radius = 6378137.0 // approximate Earth radius, *in meters*
        val deltaLat = toLat - fromLat
        val deltaLon = toLon - fromLon
        val angle = 2 * Math.asin(
            Math.sqrt(
                Math.pow(Math.sin(deltaLat / 2), 2.0)  
                        Math.cos(fromLat) * Math.cos(toLat) *
                        Math.pow(Math.sin(deltaLon / 2), 2.0)
            )
        )
        return radius * angle
    }

    init {
        currentLat = currentLatitude
        currentLng = currentLongitude
    }
}

fun main(args: Array<String>) {
    val command = Scanner(System.`in`)
    val running = true

    while (running) {

        val inputStream: InputStream = File("./src/main/kotlin/suburbs.json").inputStream()
        val inputString = inputStream.bufferedReader().use { it.readText() }
        val gson = GsonBuilder().create()
        val packagesArray = gson.fromJson(inputString , Array<Property>::class.java).toList()

        println("Please enter a suburb name: ")
        val suburbName = command.nextLine()

        println("Please enter the postcode: ")
        val postcode = command.nextLine()

        val userProperty: Property? = packagesArray.find{ it.Locality.toLowerCase().equals(suburbName.toLowerCase()) && it.Pcode == postcode.toInt()}

        //sort the list, give the Comparator the current location
        Collections.sort(packagesArray, new SortPlaces(userProperty.Latitude, userProperty.Longitude));


    }
    command.close()
}

I got error: Too many arguments for public open fun <T : Comparable<T!>!> sort(list: (Mutable)List<T!>!): Unit defined in java.util.Collections at my sort{} function my userProperty has to be Property? because the find{} method return Property? Then Collections.sort() can not sort Property? type because the SortPLaces only accept Comparator not Comparator<Property?> What should I do?

CodePudding user response:

There are multiple errors in your code. To create a new object in Kotlin, you don't write the word new like you do in Java. Also, as you have noticed, find returns a nullable type - Property?. You need to check for nulls when using userProperty. A Property matching the criteria you want may not necessarily be found, after all.

if (userProperty != null) {
    Collections.sort(packagesArray, SortPlaces(userProperty.Latitude, userProperty.Longitude))
} else {
    // no property is found! Think about what you should do in such a case
}

Since you are sorting the list in line, you should not make an immutable list with toList when you are deserialising the JSON, but rather a MutableList:

val packagesArray = gson.fromJson(inputString, Array<Property>::class.java).toMutableList()

Also, you seem to be using a lot of Java APIs. In Kotlin, a lot of the Java APIs that you are using have more idiomatic Kotlin counterparts. To sort the list, you don't need the SortPlaces class at all. Simply use sortBy on the array, and call your distance function in the lambda.

data class Property(
    val pcode: Int,
    val locality: String,
    val state: String,
    val comments: String,
    val category: String,
    val longitude: Double,
    val latitude: Double,
    )


fun distance(fromLat: Double, fromLon: Double, toLat: Double, toLon: Double): Double {
    val radius = 6378137.0 // approximate Earth radius, *in meters*
    val deltaLat = toLat - fromLat
    val deltaLon = toLon - fromLon
    val angle = 2 * asin(
        sqrt(
            sin(deltaLat / 2).pow(2.0)  
                    cos(fromLat) * cos(toLat) *
                    sin(deltaLon / 2).pow(2.0)
        )
    )
    return radius * angle
}

fun main(args: Array<String>) {
    val running = true

    while (running) {
        val inputStream = File("./src/main/kotlin/suburbs.json").inputStream()
        val inputString = inputStream.bufferedReader().use { it.readText() }
        val gson = GsonBuilder().create()
        val packagesArray = gson.fromJson(inputString , Array<Property>::class.java).toMutableList()

        println("Please enter a suburb name: ")
        val suburbName = readLine()

        println("Please enter the postcode: ")
        val postcode = readLine()

        val userProperty = packagesArray.find {
            it.locality.lowercase() == suburbName?.lowercase() && it.pcode == postcode?.toInt()
        }

        //sort the list, give the Comparator the current location
        if (userProperty != null) {
            packagesArray.sortBy {
                distance(userProperty.latitude, userProperty.longitude, it.latitude, it.longitude)
            }
        } else {
            // did not find such a property!
        }

    }
}
  •  Tags:  
  • Related