Search code examples
scalamultidimensional-arrayhaversine

I have an array of array and passing it to a function in scala but getting errors


I need to implement a distance search code.My input is as follows in the CSV.

Proprty_ID,  lat,    lon
123,    33.84,  -118.39
234,    35.89,  -119.48
345,    35.34,  -119.39

I have a haversine formula which takes 2 coordinates (lat1, lon1), (lat2, lon2) and return the distance. Let say:

val Distance: Double = haversine(x1:Double, x2:Double, y1:Double, y2:Double)

I need to find out the distance of each property with each other. so the output will look like this.

Property_ID1, Property_ID2, distance
123,123,0
123,234,0.1
123,345,0.6
234,234,0
234,123,0.1
234,345,0.7
345,345,0
345,123,0.6
345,234,0.7

How can I implement this in Scala?

import math._

object Haversine {
   val R = 6372.8  //radius in km

   def haversine(lat1:Double, lon1:Double, lat2:Double, lon2:Double)={
      val dLat=(lat2 - lat1).toRadians
      val dLon=(lon2 - lon1).toRadians

      val a = pow(sin(dLat/2),2) + pow(sin(dLon/2),2) * cos(lat1.toRadians) * cos(lat2.toRadians)
      val c = 2 * asin(sqrt(a))
      R * c
   }

   def main(args: Array[String]): Unit = {
      println(haversine(36.12, -86.67, 33.94, -118.40))
  }
}

class SimpleCSVHeader(header:Array[String]) extends Serializable {
  val index = header.zipWithIndex.toMap
  def apply(array:Array[String], key:String):String = array(index(key))
}

val lat1=33.84
val lon1=-118.39
val csv = sc.textFile("file.csv") 
val data = csv.map(line => line.split(",").map(elem => elem.trim)) 
val header = new SimpleCSVHeader(data.take(1)(0)) // we build our header with the first line
val rows = data.filter(line => header(line,"lat") != "lat") // filter the header out

// I will do the looping for all properties here but I am trying to get the map function right for one property at least
val distances = rows.map(x => haversine(x.take(1)(0).toDouble,x.take(1)(1).toDouble, lat1,lon1)`

Now this should give me the distances for all the properties from (lat1, lon1). I know it's not right but I am not able to think from here.


Solution

  • I'd try to break it down into steps. Given data like:

      val rows = List(Array("123", "33.84", "-118.39"),
                      Array("234", "35.89", "-119.48"),
                      Array("345", "35.34", "-119.39"))
    

    First convert the types:

      val typed = rows.map{ case Array(id, lat, lon) => (id, lat.toDouble, lon.toDouble)}
    

    Then generate the combinations:

      val combos = for {
        a <- typed
        b <- typed
      } yield (a,b)
    

    Then generate an output line for each combination:

      combos.map{ case ((id1, lat1, lon1), (id2, lat2, lon2)) 
         => id1 + "," + id2 + "," + haversine(lat1, lon1, lat2, lon2)} foreach println