From 194067e976da0eefd22dc3fbd532b916dc7ee16f Mon Sep 17 00:00:00 2001 From: Xavier GUIHOT Date: Mon, 15 Jan 2018 23:53:23 +0100 Subject: [PATCH] Refactoring using for comprehensions --- README.md | 26 +- build.sbt | 4 +- docs/com/geobase/GeoBase.html | 104 ++--- docs/com/geobase/package.html | 14 +- project/plugins.sbt | 9 +- src/main/scala/com/geobase/GeoBase.scala | 407 ++++++------------ src/main/scala/com/geobase/load/Loader.scala | 3 +- .../scala/com/geobase/model/Airline.scala | 11 +- .../com/geobase/model/AirportOrCity.scala | 69 ++- .../scala/com/geobase/model/Country.scala | 33 +- src/test/scala/com/geobase/GeoBaseTest.scala | 4 +- 11 files changed, 250 insertions(+), 434 deletions(-) diff --git a/README.md b/README.md index 362b876..04b3e20 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ ## Overview -Version: 1.1.2 +Version: 1.1.3 API Scaladoc: [GeoBase](http://xavierguihot.com/geobase/#com.geobase.GeoBase) @@ -46,19 +46,19 @@ import com.geobase.GeoBase val geoBase = new GeoBase() -assert(geoBase.getCityFor("CDG").get == "PAR") -assert(geoBase.getCountryFor("CDG").get == "FR") -assert(geoBase.getCountryForAirline("AF").get == "FR") -assert(geoBase.getCurrencyFor("NYC").get == "USD") -assert(geoBase.getDistanceBetween("PAR", "NCE").get == 686) -assert(geoBase.getTripDurationFromLocalDates("20160606_1627", "CDG", "20160606_1757", "JFK").get == 7.5d) -assert(geoBase.getNearbyAirports("CDG", 50).get == List("LBG", "ORY", "VIY", "POX")) +assert(geoBase.getCityFor("CDG") == Success("PAR")) +assert(geoBase.getCountryFor("CDG") == Success("FR")) +assert(geoBase.getCountryForAirline("AF") == Success("FR")) +assert(geoBase.getCurrencyFor("NYC") == Success("USD")) +assert(geoBase.getDistanceBetween("PAR", "NCE") == Success(686)) +assert(geoBase.getTripDurationFromLocalDates("20160606_1627", "CDG", "20160606_1757", "JFK") == Success(7.5d)) +assert(geoBase.getNearbyAirports("CDG", 50) == Success(List("LBG", "ORY", "VIY", "POX"))) ``` Getters all have a return type embedded within the Try monade. Throwing exceptions when one might request mappings for non existing locations, isn't -realy the idiomatic of scala, and simply embedding the result in the Option -monad doesn't give the user the possibility to understand what went wrong. +realy the idiomatic scala way, and simply embedding the result in the Option +monade doesn't give the user the possibility to understand what went wrong. Thus the usage of the Try monade. @@ -70,7 +70,7 @@ With sbt, add these lines to your build.sbt: ```scala resolvers += "jitpack" at "https://jitpack.io" -libraryDependencies += "com.github.xavierguihot" % "geobase" % "v1.1.2" +libraryDependencies += "com.github.xavierguihot" % "geobase" % "v1.1.3" ``` With maven, add these lines to your pom.xml: @@ -86,7 +86,7 @@ With maven, add these lines to your pom.xml: com.github.xavierguihot geobase - v1.1.2 + v1.1.3 ``` @@ -100,7 +100,7 @@ allprojects { } dependencies { - compile 'com.github.xavierguihot:geobase:v1.1.2' + compile 'com.github.xavierguihot:geobase:v1.1.3' } ``` diff --git a/build.sbt b/build.sbt index 5cee3fc..3bc9689 100644 --- a/build.sbt +++ b/build.sbt @@ -1,6 +1,6 @@ name := "geobase" -version := "1.1.2" +version := "1.1.3" scalaVersion := "2.11.8" @@ -15,4 +15,6 @@ assemblyJarName in assembly := name.value + "-" + version.value + ".jar" assemblyOutputPath in assembly := file("./" + name.value + "-" + version.value + ".jar") +libraryDependencies += "org.typelevel" %% "cats-core" % "1.0.1" + libraryDependencies += "org.scalatest" %% "scalatest" % "3.0.1" % "test" diff --git a/docs/com/geobase/GeoBase.html b/docs/com/geobase/GeoBase.html index 8f7d6f2..012c37e 100644 --- a/docs/com/geobase/GeoBase.html +++ b/docs/com/geobase/GeoBase.html @@ -57,13 +57,13 @@

val geoBase = new GeoBase() -assert(geoBase.getCityFor("CDG").get == "PAR") -assert(geoBase.getCountry("CDG").get == "FR") -assert(geoBase.getCurrencyFor("NYC").get == "USD") -assert(geoBase.getCountryForAirline("AF").get == "FR") -assert(geoBase.getDistanceBetween("PAR", "NCE").get == 686) -assert(geoBase.getTripDurationFromLocalDates("20160606_1627", "CDG", "20160606_1757", "JFK").get == 7.5d) -assert(geoBase.getNearbyAirports("CDG", 50).get == List("LBG", "ORY", "VIY", "POX"))

The GeoBase object can be used within Spark jobs (in this case, don't forget +assert(geoBase.getCityFor("CDG") == Success("PAR")) +assert(geoBase.getCountry("CDG") == Success("FR")) +assert(geoBase.getCurrencyFor("NYC") == Success("USD")) +assert(geoBase.getCountryForAirline("AF") == Success("FR")) +assert(geoBase.getDistanceBetween("PAR", "NCE") == Success(686)) +assert(geoBase.getTripDurationFromLocalDates("20160606_1627", "CDG", "20160606_1757", "JFK") == Success(7.5d)) +assert(geoBase.getNearbyAirports("CDG", 50) == Success(List("LBG", "ORY", "VIY", "POX")))

The GeoBase object can be used within Spark jobs (in this case, don't forget the possibility to broadcast GeoBase).

Opentraveldata is an accurate and maintained source of various travel mappings. This scala wrapper around opentraveldata mostly uses this file: @@ -306,10 +306,8 @@

method, returns this list of cities (usually the list will only contain one city).

The method getCityFor returns the first city corresponding to the given airport, which is by assumption the biggest corresponding city.

assert(geoBase.getCitiesFor("CDG") == Success(List("PAR")))
-assert(geoBase.getCitiesFor("CDG").get == List("PAR"))
 assert(geoBase.getCitiesFor("AZA") == Success(List("PHX", "MSC")))
-assert(geoBase.getCitiesFor("?*#") == Failure(GeoBaseException: Unknown airport "?*#")
-assert(geoBase.getCitiesFor("?*#").getOrElse(Nil) == List())
airport

the airport IATA code (for instance AZA) for which to get +assert(geoBase.getCitiesFor("?*#") == Failure(GeoBaseException: Unknown airport "?*#")

airport

the airport IATA code (for instance AZA) for which to get the associated cities.

returns

the list of city codes corresponding to the given airport (for instance List("PHX", "MSC")).

  • @@ -329,9 +327,7 @@

    Returns the city associated to the given airport.

    Returns the city associated to the given airport.

    assert(geoBase.getCityFor("CDG") == Success("PAR"))
    -assert(geoBase.getCityFor("CDG").get == "PAR")
    -assert(geoBase.getCityFor("?*#") == Failure(GeoBaseException: Unknown airport "?*#")
    -assert(geoBase.getCityFor("?*#").getOrElse("") == "")
    airport

    the airport IATA code (for instance CDG) for which to get +assert(geoBase.getCityFor("?*#") == Failure(GeoBaseException: Unknown airport "?*#")

    airport

    the airport IATA code (for instance CDG) for which to get the associated city.

    returns

    the city code corresponding to the given airport (for instance PAR).

  • @@ -369,10 +365,9 @@

    Returns the continent associated to the given airport, city or country.

    Returns the continent associated to the given airport, city or country.

    Possible values: EU (Eurrope) - NA (North America) - SA (South Africa) - AF (Africa) - AS (Asia) - AN (Antarctica) - OC (Oceania).

    assert(geoBase.getContinentFor("CDG") == Success("EU")) // location is an airport
    -assert(geoBase.getContinentFor("NYC").get == "NA") // location is a city
    -assert(geoBase.getContinentFor("CN").get == "AS") // location is a country
    -assert(geoBase.getContinentFor("?*#") == Failure(GeoBaseException: Unknown location "?*#"))
    -assert(geoBase.getContinentFor("?*#").getOrElse("") == "")
    location

    the country, city or airport IATA code (for instance +assert(geoBase.getContinentFor("NYC") == Success("NA")) // location is a city +assert(geoBase.getContinentFor("CN") == Success("AS")) // location is a country +assert(geoBase.getContinentFor("?*#") == Failure(GeoBaseException: Unknown location "?*#"))

    location

    the country, city or airport IATA code (for instance PAR) for which to get the associated continent.

    returns

    the continent code corresponding to the given location (for instance EU).

  • @@ -392,10 +387,8 @@

    Returns the country associated to the given location (city or airport).

    Returns the country associated to the given location (city or airport).

    assert(geoBase.getCountryFor("PAR") == Success("FR"))
    -assert(geoBase.getCountryFor("PAR").get == "FR")
    -assert(geoBase.getCountryFor("ORY").get == "FR")
    -assert(geoBase.getCountryFor("?*#") == Failure(GeoBaseException: Unknown location "?*#"))
    -assert(geoBase.getCountryFor("?*#").getOrElse("") == "")
    location

    the location IATA code (city or airport - for instance +assert(geoBase.getCountryFor("ORY") == Success("FR")) +assert(geoBase.getCountryFor("?*#") == Failure(GeoBaseException: Unknown location "?*#"))

    location

    the location IATA code (city or airport - for instance PAR) for which to get the associated country.

    returns

    the country code corresponding to the given city or airport (for instance FR).

  • @@ -415,9 +408,7 @@

    Returns the country associated to the given airline.

    Returns the country associated to the given airline.

    assert(geoBase.getCountryForAirline("AF") == Success("FR"))
    -assert(geoBase.getCountryForAirline("AF").get == "FR")
    -assert(geoBase.getCountryForAirline("#?") == Failure(GeoBaseException: Unknown airline "#?"))
    -assert(geoBase.getCountryForAirline("#?").getOrElse("") == "")
    airline

    the airline IATA code (for instance AF) for which to get +assert(geoBase.getCountryForAirline("#?") == Failure(GeoBaseException: Unknown airline "#?"))

    airline

    the airline IATA code (for instance AF) for which to get the associated country.

    returns

    the country code corresponding to the given airline (for instance FR).

  • @@ -437,9 +428,8 @@

    Returns the currency associated to the given location (airport, city or country).

    Returns the currency associated to the given location (airport, city or country).

    assert(geoBase.getCurrencyFor("JFK") == Success("USD"))
    -assert(geoBase.getCurrencyFor("FR").get == "EUR")
    -assert(geoBase.getCurrencyFor("?#").get == Failure(GeoBaseException: Unknown country "#?"))
    -assert(geoBase.getCurrencyFor("?#").getOrElse("USD") == "USD")
    location

    the country, city or airport IATA code (for instance FR) +assert(geoBase.getCurrencyFor("FR") == Success("EUR")) +assert(geoBase.getCurrencyFor("?#") == Failure(GeoBaseException: Unknown country "#?"))

    location

    the country, city or airport IATA code (for instance FR) for which to get the associated currency.

    returns

    the currency code corresponding to the given location (for instance EUR).

  • @@ -459,9 +449,8 @@

    Returns the distance between two locations (airports/cities).

    Returns the distance between two locations (airports/cities).

    assert(geoBase.getDistanceBetween("ORY", "NCE") == Success(674))
    -assert(geoBase.getDistanceBetween("PAR", "NCE").get == 686)
    -assert(geoBase.getDistanceBetween("PAR", "~#?").get == Failure(GeoBaseException: Unknown location "~#?"))
    -assert(geoBase.getDistanceBetween("PAR", "~#?").getOrElse(-1) == -1)
    locationA

    an airport or city IATA code (for instance ORY) for +assert(geoBase.getDistanceBetween("PAR", "NCE") == Success(686)) +assert(geoBase.getDistanceBetween("PAR", "~#?") == Failure(GeoBaseException: Unknown location "~#?"))

    locationA

    an airport or city IATA code (for instance ORY) for which to get the distance with locationB.

    locationB

    an airport or city IATA code (for instance NCE) for which to get the distance with locationA.

    returns

    the distance rounded in km between locationA and locationB (for instance 674 km).

    @@ -484,12 +473,10 @@

    Returns the geo type of a trip (domestic, continental or inter continental).

    Returns the geo type of a trip (domestic, continental or inter continental).

    Return an enum value: GeoType.DOMESTIC, GeoType.CONTINENTAL or GeoType.INTER_CONTINENTAL.

    The distinction between continental and intercontinental is made based on iata zones.

    assert(geoBase.getGeoType(List("CDG", "ORY")) == Success(GeoType.DOMESTIC))
    -assert(geoBase.getGeoType(List("CDG", "ORY")).get == GeoType.DOMESTIC)
    -assert(geoBase.getGeoType(List("FR", "FR")).get == GeoType.DOMESTIC)
    -assert(geoBase.getGeoType(List("FR", "PAR", "DUB")).get == GeoType.CONTINENTAL)
    -assert(geoBase.getGeoType(List("CDG", "TLS", "JFK", "MEX")).get == GeoType.INTER_CONTINENTAL)
    -assert(geoBase.getGeoType(List("US", "bbb", "NCE", "aaa")).get == Failure(GeoBaseException: Unknown locations \"bbb\", \"aaa\"))
    -assert(geoBase.getGeoType(List("US", "bbb", "NCE", "aaa")).toOption == None)
    locations

    a list of cities/ariports/countries representing the +assert(geoBase.getGeoType(List("FR", "FR")) == Success(GeoType.DOMESTIC)) +assert(geoBase.getGeoType(List("FR", "PAR", "DUB")) == Success(GeoType.CONTINENTAL)) +assert(geoBase.getGeoType(List("CDG", "TLS", "JFK", "MEX")) == Success(GeoType.INTER_CONTINENTAL)) +assert(geoBase.getGeoType(List("US", "bbb", "NCE", "aaa")) == Failure(GeoBaseException: Unknown locations \"bbb\", \"aaa\"))

    locations

    a list of cities/ariports/countries representing the trip.

    returns

    the type of the trip (a GeoType enum value, such as GeoType.DOMESTIC).

  • @@ -509,10 +496,9 @@

    Returns the IATA zone associated to the given airport, city or country.

    Returns the IATA zone associated to the given airport, city or country.

    Possible values are 11, 12, 13, 21, 22, 23, 31, 32 or 33.

    assert(geoBase.getIataZoneFor("CDG") == Success("21"))
    -assert(geoBase.getIataZoneFor("NYC").get == "11")
    -assert(geoBase.getIataZoneFor("ZA").get == "23")
    -assert(geoBase.getIataZoneFor("?*#") == Failure(GeoBaseException: Unknown location "?*#"))
    -assert(geoBase.getIataZoneFor("?*#").getOrElse("") == "")
    location

    the country, city or airport IATA code (for instance +assert(geoBase.getIataZoneFor("NYC") == Success("11")) +assert(geoBase.getIataZoneFor("ZA") == Success("23")) +assert(geoBase.getIataZoneFor("?*#") == Failure(GeoBaseException: Unknown location "?*#"))

    location

    the country, city or airport IATA code (for instance PAR) for which to get the associated IATA zone.

    returns

    the IATA zone code corresponding to the given location (for instance 21).

  • @@ -533,9 +519,8 @@

    Returns the list of nearby airports (within the radius) for the given airport or city.

    Returns the list of nearby airports (within the radius) for the given airport or city.

    Find the list of nearby airports, within the requested radius. The list is sorted starting from the closest airport.

    assert(geoBase.getNearbyAirportsWithDetails("CDG", 50) == Success(List("LBG", "ORY", "VIY", "POX")))
    -assert(geoBase.getNearbyAirportsWithDetails("CDG", 36).get == List("LBG", "ORY"))
    -assert(geoBase.getNearbyAirportsWithDetails("~#?", 36)).get == Failure(GeoBaseException: Unknown location \"~#?\""))
    -assert(geoBase.getNearbyAirportsWithDetails("~#?", 36)).getOrElse(Nil) == List())
    location

    the airport or city for which to find nearby airports.

    radius

    the maximum distance (in km) for which an airport is +assert(geoBase.getNearbyAirportsWithDetails("CDG", 36) == Success(List("LBG", "ORY"))) +assert(geoBase.getNearbyAirportsWithDetails("~#?", 36)) == Failure(GeoBaseException: Unknown location \"~#?\""))

    location

    the airport or city for which to find nearby airports.

    radius

    the maximum distance (in km) for which an airport is considered close.

    returns

    the sorted per incresaing distance list nearby airports

  • @@ -556,9 +541,8 @@

    Returns the list of nearby airports (within the radius) for the given airport or city.

    Returns the list of nearby airports (within the radius) for the given airport or city.

    Find the list of nearby airports, within the requested radius. The list is sorted starting from the closest airport. This list is a tuple of (airport/distance).

    assert(geoBase.getNearbyAirportsWithDetails("CDG", 50) == Success(List(("LBG", 9), ("ORY", 35), ("VIY", 37), ("POX", 38))))
    -assert(geoBase.getNearbyAirportsWithDetails("CDG", 36).get == List(("LBG", 9), ("ORY", 35)))
    -assert(geoBase.getNearbyAirportsWithDetails("~#?", 36)).get == Failure(GeoBaseException: Unknown location \"~#?\""))
    -assert(geoBase.getNearbyAirportsWithDetails("~#?", 36)).getOrElse(Nil) == List())
    location

    the airport or city for which to find nearby +assert(geoBase.getNearbyAirportsWithDetails("CDG", 36) == Success(List(("LBG", 9), ("ORY", 35)))) +assert(geoBase.getNearbyAirportsWithDetails("~#?", 36)) == Failure(GeoBaseException: Unknown location \"~#?\""))

    location

    the airport or city for which to find nearby airports.

    radius

    the maximum distance (in km) for which an airport is considered close.

    returns

    the sorted per incresaing distance list of tuples (airport, distance).

    @@ -579,12 +563,10 @@

    Returns the offset in minutes for the given date at the given city/airport.

    Returns the offset in minutes for the given date at the given city/airport.

    assert(geoBase.getOffsetForLocalDate("20170712", "NCE") == Success(120))
    -assert(geoBase.getOffsetForLocalDate("20170712", "NCE").get == 120)
    -assert(geoBase.getOffsetForLocalDate("2017-07-12", "NCE", "yyyy-MM-dd").get == 120)
    -assert(geoBase.getOffsetForLocalDate("20171224", "NCE").get == 60)
    -assert(geoBase.getOffsetForLocalDate("20171224", "NYC").get == -300)
    -assert(geoBase.getOffsetForLocalDate("20171224", "~#?") == Failure(GeoBaseException: Unknown location "~#?"))
    -assert(geoBase.getOffsetForLocalDate("20171224", "~#?").getOrElse("0") == 0)
    localDate

    the local date

    location

    the airport or the city where this local date applies

    format

    (default = "yyyyMMdd") the format under which localDate is +assert(geoBase.getOffsetForLocalDate("2017-07-12", "NCE", "yyyy-MM-dd") == Success(120)) +assert(geoBase.getOffsetForLocalDate("20171224", "NCE") == Success(60)) +assert(geoBase.getOffsetForLocalDate("20171224", "NYC") == Success(-300)) +assert(geoBase.getOffsetForLocalDate("20171224", "~#?") == Failure(GeoBaseException: Unknown location "~#?"))

    localDate

    the local date

    location

    the airport or the city where this local date applies

    format

    (default = "yyyyMMdd") the format under which localDate is provided.

    returns

    the the offset in minutes for the given date at the given city/airport (can be negative).

  • @@ -607,16 +589,14 @@

    flying time (EFT).

    This is meant to be used to compute the trip duration for a segment/bound for which we know the origin/destination airports/cities and the local time. i.e. when we don't have gmt times.

    assert(geoBase.getTripDurationFromLocalDates("20160606_1627", "CDG", "20160606_1757", "JFK") == Success(7.5d))
    -assert(geoBase.getTripDurationFromLocalDates("20160606_1627", "CDG", "20160606_1757", "JFK").get == 7.5d)
     
     val computedTripDuration = geoBase.getTripDurationFromLocalDates(
     	"2016-06-06T16:27", "CDG", "2016-06-06T17:57", "JFK",
     	format = "yyyy-MM-dd'T'HH:mm", unit = "minutes"
     )
    -assert(computedTripDuration.get == 450d)
    +assert(computedTripDuration == Success(450d))
     
    -assert(geoBase.getTripDurationFromLocalDates("20160606_1627", "~#?", "20160606_1757", "JFK").get == Failure(GeoBaseException: Unknown location "~#?"))
    -assert(geoBase.getTripDurationFromLocalDates("20160606_1627", "~#?", "20160606_1757", "JFK").getOrElse(-1d) == -1d)
    localDepartureDate

    the departure local date

    originLocation

    the origin airport or city

    localArrivalDate

    the arrival local date

    destinationLocation

    the destination airport or city

    unit

    (default = "hours") either "hours" or "minutes"

    format

    (default = "yyyyMMdd_HHmm") the format under which local +assert(geoBase.getTripDurationFromLocalDates("20160606_1627", "~#?", "20160606_1757", "JFK") == Failure(GeoBaseException: Unknown location "~#?"))

    localDepartureDate

    the departure local date

    originLocation

    the origin airport or city

    localArrivalDate

    the arrival local date

    destinationLocation

    the destination airport or city

    unit

    (default = "hours") either "hours" or "minutes"

    format

    (default = "yyyyMMdd_HHmm") the format under which local departure and arrival dates are provided.

    returns

    the trip duration in the chosen unit (in hours by default) and format.

  • @@ -639,10 +619,8 @@

    additional value is the knowledge of the time zone thanks to opentraveldata. You don't need to know the time zone, just enter the airport or the city as a parameter.

    assert(geoBase.gmtDateToLocal("20160606_1427", "NCE") == Success("20160606_1627"))
    -assert(geoBase.gmtDateToLocal("20160606_1427", "NCE").get == "20160606_1627")
    -assert(geoBase.gmtDateToLocal("2016-06-07T02:27", "NYC", "yyyy-MM-dd'T'HH:mm").get == "2016-06-06T22:27")
    -assert(geoBase.gmtDateToLocal("20160606_2227", "~#?") == Failure(GeoBaseException: Unknown location "~#?"))
    -assert(geoBase.gmtDateToLocal("20160606_2227", "~#?").getOrElse("20000101_1200") == "20000101_1200")
    gmtDate

    the GMT date

    location

    the airport or the city where this GMT date is to be +assert(geoBase.gmtDateToLocal("2016-06-07T02:27", "NYC", "yyyy-MM-dd'T'HH:mm") == Success("2016-06-06T22:27")) +assert(geoBase.gmtDateToLocal("20160606_2227", "~#?") == Failure(GeoBaseException: Unknown location "~#?"))

    gmtDate

    the GMT date

    location

    the airport or the city where this GMT date is to be localized.

    format

    (default = "yyyyMMdd_HHmm") the format under which gmtDate is provided and the local date is returned.

    returns

    the local date associated to the GMT date under the requested format.

    @@ -700,10 +678,8 @@

    additional value is the knowledge of the time zone thanks to opentraveldata. You don't need to know the time zone, just enter the airport or the city as a parameter.

    assert(geoBase.localDateToGMT("20160606_2227", "NYC") == Success("20160607_0227"))
    -assert(geoBase.localDateToGMT("20160606_2227", "NYC").get == "20160607_0227")
    -assert(geoBase.localDateToGMT("2016-06-06T22:27", "NYC", "yyyy-MM-dd'T'HH:mm").get == "2016-06-07T02:27")
    -assert(geoBase.localDateToGMT("20160606_2227", "~#?") == Failure(GeoBaseException: Unknown location "~#?"))
    -assert(geoBase.localDateToGMT("20160606_2227", "~#?").getOrElse("20000101_1200") == "20000101_1200")
    localDate

    the local date at the given location under the given +assert(geoBase.localDateToGMT("2016-06-06T22:27", "NYC", "yyyy-MM-dd'T'HH:mm") == Success("2016-06-07T02:27")) +assert(geoBase.localDateToGMT("20160606_2227", "~#?") == Failure(GeoBaseException: Unknown location "~#?"))

    localDate

    the local date at the given location under the given format.

    location

    the airport or city where this local date applies

    format

    (default = "yyyyMMdd_HHmm") the format under which localDate is provided and the GMT date is returned.

    returns

    the GMT date associated to the local date under the requested format.

    diff --git a/docs/com/geobase/package.html b/docs/com/geobase/package.html index e436245..7635a98 100644 --- a/docs/com/geobase/package.html +++ b/docs/com/geobase/package.html @@ -90,13 +90,13 @@

    val geoBase = new GeoBase() -assert(geoBase.getCityFor("CDG").get == "PAR") -assert(geoBase.getCountry("CDG").get == "FR") -assert(geoBase.getCurrencyFor("NYC").get == "USD") -assert(geoBase.getCountryForAirline("AF").get == "FR") -assert(geoBase.getDistanceBetween("PAR", "NCE").get == 686) -assert(geoBase.getTripDurationFromLocalDates("20160606_1627", "CDG", "20160606_1757", "JFK").get == 7.5d) -assert(geoBase.getNearbyAirports("CDG", 50).get == List("LBG", "ORY", "VIY", "POX"))

    The GeoBase object can be used within Spark jobs (in this case, don't forget +assert(geoBase.getCityFor("CDG") == Success("PAR")) +assert(geoBase.getCountry("CDG") == Success("FR")) +assert(geoBase.getCurrencyFor("NYC") == Success("USD")) +assert(geoBase.getCountryForAirline("AF") == Success("FR")) +assert(geoBase.getDistanceBetween("PAR", "NCE") == Success(686)) +assert(geoBase.getTripDurationFromLocalDates("20160606_1627", "CDG", "20160606_1757", "JFK") == Success(7.5d)) +assert(geoBase.getNearbyAirports("CDG", 50) == Success(List("LBG", "ORY", "VIY", "POX")))

    The GeoBase object can be used within Spark jobs (in this case, don't forget the possibility to broadcast GeoBase).

    Opentraveldata is an accurate and maintained source of various travel mappings. This scala wrapper around opentraveldata mostly uses this file: diff --git a/project/plugins.sbt b/project/plugins.sbt index 024f1f0..d0bfd05 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1,4 +1,5 @@ -addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.14.2") -addSbtPlugin("org.scoverage" % "sbt-scoverage" % "1.5.1") -addSbtPlugin("org.scoverage" % "sbt-coveralls" % "1.2.2") -addSbtPlugin("org.wartremover" % "sbt-wartremover" % "2.2.1") +addSbtPlugin("org.lyranthe.sbt" % "partial-unification" % "1.1.0") +addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.14.2") +addSbtPlugin("org.scoverage" % "sbt-scoverage" % "1.5.1") +addSbtPlugin("org.scoverage" % "sbt-coveralls" % "1.2.2") +addSbtPlugin("org.wartremover" % "sbt-wartremover" % "2.2.1") diff --git a/src/main/scala/com/geobase/GeoBase.scala b/src/main/scala/com/geobase/GeoBase.scala index eec45de..e2e971c 100644 --- a/src/main/scala/com/geobase/GeoBase.scala +++ b/src/main/scala/com/geobase/GeoBase.scala @@ -11,10 +11,10 @@ import java.text.SimpleDateFormat import java.util.concurrent.TimeUnit import java.util.TimeZone -import java.security.InvalidParameterException - import math.{asin, cos, pow, round, sin, sqrt} +import cats.implicits._ + /** A facility to '''deal with travel/geographical data'''. * * Provides '''geographical mappings''' at airport/city/country level mainly @@ -30,13 +30,13 @@ import math.{asin, cos, pow, round, sin, sqrt} * * val geoBase = new GeoBase() * - * assert(geoBase.getCityFor("CDG").get == "PAR") - * assert(geoBase.getCountry("CDG").get == "FR") - * assert(geoBase.getCurrencyFor("NYC").get == "USD") - * assert(geoBase.getCountryForAirline("AF").get == "FR") - * assert(geoBase.getDistanceBetween("PAR", "NCE").get == 686) - * assert(geoBase.getTripDurationFromLocalDates("20160606_1627", "CDG", "20160606_1757", "JFK").get == 7.5d) - * assert(geoBase.getNearbyAirports("CDG", 50).get == List("LBG", "ORY", "VIY", "POX")) + * assert(geoBase.getCityFor("CDG") == Success("PAR")) + * assert(geoBase.getCountry("CDG") == Success("FR")) + * assert(geoBase.getCurrencyFor("NYC") == Success("USD")) + * assert(geoBase.getCountryForAirline("AF") == Success("FR")) + * assert(geoBase.getDistanceBetween("PAR", "NCE") == Success(686)) + * assert(geoBase.getTripDurationFromLocalDates("20160606_1627", "CDG", "20160606_1757", "JFK") == Success(7.5d)) + * assert(geoBase.getNearbyAirports("CDG", 50) == Success(List("LBG", "ORY", "VIY", "POX"))) * }}} * * The GeoBase object can be used within Spark jobs (in this case, don't forget @@ -73,9 +73,7 @@ class GeoBase() extends Serializable { * * {{{ * assert(geoBase.getCityFor("CDG") == Success("PAR")) - * assert(geoBase.getCityFor("CDG").get == "PAR") * assert(geoBase.getCityFor("?*#") == Failure(GeoBaseException: Unknown airport "?*#") - * assert(geoBase.getCityFor("?*#").getOrElse("") == "") * }}} * * @param airport the airport IATA code (for instance CDG) for which to get @@ -83,12 +81,9 @@ class GeoBase() extends Serializable { * @return the city code corresponding to the given airport (for instance * PAR). */ - def getCityFor(airport: String): Try[String] = { - - airportsAndCities.get(airport) match { - case Some(airportInfo) => airportInfo.getCity() - case None => Failure(GeoBaseException("Unknown airport \"" + airport + "\"")) - } + def getCityFor(airport: String): Try[String] = airportsAndCities.get(airport) match { + case Some(airportInfo) => airportInfo.getCity() + case None => Failure(GeoBaseException("Unknown airport \"" + airport + "\"")) } /** Returns the cities associated to the given airport. @@ -102,10 +97,8 @@ class GeoBase() extends Serializable { * * {{{ * assert(geoBase.getCitiesFor("CDG") == Success(List("PAR"))) - * assert(geoBase.getCitiesFor("CDG").get == List("PAR")) * assert(geoBase.getCitiesFor("AZA") == Success(List("PHX", "MSC"))) * assert(geoBase.getCitiesFor("?*#") == Failure(GeoBaseException: Unknown airport "?*#") - * assert(geoBase.getCitiesFor("?*#").getOrElse(Nil) == List()) * }}} * * @param airport the airport IATA code (for instance AZA) for which to get @@ -113,22 +106,17 @@ class GeoBase() extends Serializable { * @return the list of city codes corresponding to the given airport (for * instance List("PHX", "MSC")). */ - def getCitiesFor(airport: String): Try[List[String]] = { - - airportsAndCities.get(airport) match { - case Some(airportInfo) => airportInfo.getCities() - case None => Failure(GeoBaseException("Unknown airport \"" + airport + "\"")) - } + def getCitiesFor(airport: String): Try[List[String]] = airportsAndCities.get(airport) match { + case Some(airportInfo) => airportInfo.getCities() + case None => Failure(GeoBaseException("Unknown airport \"" + airport + "\"")) } /** Returns the country associated to the given location (city or airport). * * {{{ * assert(geoBase.getCountryFor("PAR") == Success("FR")) - * assert(geoBase.getCountryFor("PAR").get == "FR") - * assert(geoBase.getCountryFor("ORY").get == "FR") + * assert(geoBase.getCountryFor("ORY") == Success("FR")) * assert(geoBase.getCountryFor("?*#") == Failure(GeoBaseException: Unknown location "?*#")) - * assert(geoBase.getCountryFor("?*#").getOrElse("") == "") * }}} * * @param location the location IATA code (city or airport - for instance @@ -136,21 +124,18 @@ class GeoBase() extends Serializable { * @return the country code corresponding to the given city or airport (for * instance FR). */ - def getCountryFor(location: String): Try[String] = { - - location.length match { + def getCountryFor(location: String): Try[String] = location.length match { - // If it's already a country-like code: - case 2 => Success(location) - - // If it's a city/airport code, we transform it to a country: - case 3 => airportsAndCities.get(location) match { - case Some(locationInfo) => locationInfo.getCountry() - case None => Failure(GeoBaseException("Unknown location \"" + location + "\"")) - } + // If it's already a country-like code: + case 2 => Success(location) - case _ => Failure(GeoBaseException("Unknown location \"" + location + "\"")) + // If it's a city/airport code, we transform it to a country: + case 3 => airportsAndCities.get(location) match { + case Some(locationInfo) => locationInfo.getCountry() + case None => Failure(GeoBaseException("Unknown location \"" + location + "\"")) } + + case _ => Failure(GeoBaseException("Unknown location \"" + location + "\"")) } /** Returns the continent associated to the given airport, city or country. @@ -160,10 +145,9 @@ class GeoBase() extends Serializable { * * {{{ * assert(geoBase.getContinentFor("CDG") == Success("EU")) // location is an airport - * assert(geoBase.getContinentFor("NYC").get == "NA") // location is a city - * assert(geoBase.getContinentFor("CN").get == "AS") // location is a country + * assert(geoBase.getContinentFor("NYC") == Success("NA")) // location is a city + * assert(geoBase.getContinentFor("CN") == Success("AS")) // location is a country * assert(geoBase.getContinentFor("?*#") == Failure(GeoBaseException: Unknown location "?*#")) - * assert(geoBase.getContinentFor("?*#").getOrElse("") == "") * }}} * * @param location the country, city or airport IATA code (for instance @@ -171,20 +155,16 @@ class GeoBase() extends Serializable { * @return the continent code corresponding to the given location (for * instance EU). */ - def getContinentFor(location: String): Try[String] = { + def getContinentFor(location: String): Try[String] = for { - // We transform the location (city, airport or country) into a country: - getCountryFor(location) match { + country <- getCountryFor(location) - case Success(country) => - countries.get(country) match { - case Some(countryDetails) => countryDetails.getContinent() - case None => Failure(GeoBaseException("Unknown country \"" + country + "\"")) - } - - case Failure(exception) => Failure(exception) + continent <- countries.get(country) match { + case Some(countryDetails) => countryDetails.getContinent() + case None => Failure(GeoBaseException("Unknown country \"" + country + "\"")) } - } + + } yield continent /** Returns the IATA zone associated to the given airport, city or country. * @@ -192,10 +172,9 @@ class GeoBase() extends Serializable { * * {{{ * assert(geoBase.getIataZoneFor("CDG") == Success("21")) - * assert(geoBase.getIataZoneFor("NYC").get == "11") - * assert(geoBase.getIataZoneFor("ZA").get == "23") + * assert(geoBase.getIataZoneFor("NYC") == Success("11")) + * assert(geoBase.getIataZoneFor("ZA") == Success("23")) * assert(geoBase.getIataZoneFor("?*#") == Failure(GeoBaseException: Unknown location "?*#")) - * assert(geoBase.getIataZoneFor("?*#").getOrElse("") == "") * }}} * * @param location the country, city or airport IATA code (for instance @@ -203,28 +182,23 @@ class GeoBase() extends Serializable { * @return the IATA zone code corresponding to the given location (for * instance 21). */ - def getIataZoneFor(location: String): Try[String] = { + def getIataZoneFor(location: String): Try[String] = for { - // We transform the location (city, airport or country) into a country: - getCountryFor(location) match { + country <- getCountryFor(location) - case Success(country) => - countries.get(country) match { - case Some(countryDetails) => countryDetails.getIataZone() - case None => Failure(GeoBaseException("Unknown country \"" + country + "\"")) - } - - case Failure(exception) => Failure(exception) + iataZone <- countries.get(country) match { + case Some(countryDetails) => countryDetails.getIataZone() + case None => Failure(GeoBaseException("Unknown country \"" + country + "\"")) } - } + + } yield iataZone /** Returns the currency associated to the given location (airport, city or country). * * {{{ * assert(geoBase.getCurrencyFor("JFK") == Success("USD")) - * assert(geoBase.getCurrencyFor("FR").get == "EUR") - * assert(geoBase.getCurrencyFor("?#").get == Failure(GeoBaseException: Unknown country "#?")) - * assert(geoBase.getCurrencyFor("?#").getOrElse("USD") == "USD") + * assert(geoBase.getCurrencyFor("FR") == Success("EUR")) + * assert(geoBase.getCurrencyFor("?#") == Failure(GeoBaseException: Unknown country "#?")) * }}} * * @param location the country, city or airport IATA code (for instance FR) @@ -232,28 +206,22 @@ class GeoBase() extends Serializable { * @return the currency code corresponding to the given location (for * instance EUR). */ - def getCurrencyFor(location: String): Try[String] = { + def getCurrencyFor(location: String): Try[String] = for { - // We transform the location (city, airport or country) into a country: - getCountryFor(location) match { + country <- getCountryFor(location) - case Success(country) => - countries.get(country) match { - case Some(countryDetails) => countryDetails.getCurrency() - case None => Failure(GeoBaseException("Unknown country \"" + country + "\"")) - } - - case Failure(exception) => Failure(exception) + currency <- countries.get(country) match { + case Some(countryDetails) => countryDetails.getCurrency() + case None => Failure(GeoBaseException("Unknown country \"" + country + "\"")) } - } + + } yield currency /** Returns the country associated to the given airline. * * {{{ * assert(geoBase.getCountryForAirline("AF") == Success("FR")) - * assert(geoBase.getCountryForAirline("AF").get == "FR") * assert(geoBase.getCountryForAirline("#?") == Failure(GeoBaseException: Unknown airline "#?")) - * assert(geoBase.getCountryForAirline("#?").getOrElse("") == "") * }}} * * @param airline the airline IATA code (for instance AF) for which to get @@ -261,21 +229,17 @@ class GeoBase() extends Serializable { * @return the country code corresponding to the given airline (for * instance FR). */ - def getCountryForAirline(airline: String): Try[String] = { - - airlines.get(airline) match { - case Some(airline) => airline.getCountry() - case None => Failure(GeoBaseException("Unknown airline \"" + airline + "\"")) - } + def getCountryForAirline(airline: String): Try[String] = airlines.get(airline) match { + case Some(airline) => airline.getCountry() + case None => Failure(GeoBaseException("Unknown airline \"" + airline + "\"")) } /** Returns the distance between two locations (airports/cities). * * {{{ * assert(geoBase.getDistanceBetween("ORY", "NCE") == Success(674)) - * assert(geoBase.getDistanceBetween("PAR", "NCE").get == 686) - * assert(geoBase.getDistanceBetween("PAR", "~#?").get == Failure(GeoBaseException: Unknown location "~#?")) - * assert(geoBase.getDistanceBetween("PAR", "~#?").getOrElse(-1) == -1) + * assert(geoBase.getDistanceBetween("PAR", "NCE") == Success(686)) + * assert(geoBase.getDistanceBetween("PAR", "~#?") == Failure(GeoBaseException: Unknown location "~#?")) * }}} * * @param locationA an airport or city IATA code (for instance ORY) for @@ -285,46 +249,28 @@ class GeoBase() extends Serializable { * @return the distance rounded in km between locationA and locationB (for * instance 674 km). */ - def getDistanceBetween(locationA: String, locationB: String): Try[Int] = { + def getDistanceBetween(locationA: String, locationB: String): Try[Int] = for { - val locationDetailsA = airportsAndCities.get(locationA) - val locationDetailsB = airportsAndCities.get(locationB) - - (locationDetailsA, locationDetailsB) match { - - case (None, _) => - Failure(GeoBaseException("Unknown location \"" + locationA + "\"")) - - case (_, None) => - Failure(GeoBaseException("Unknown location \"" + locationB + "\"")) - - case (Some(locationDetailsA), Some(locationDetailsB)) => { - - val latA = locationDetailsA.getLatitude() - val lngA = locationDetailsA.getLongitude() - val latB = locationDetailsB.getLatitude() - val lngB = locationDetailsB.getLongitude() - - (latA, lngA, latB, lngB) match { + locationDetailsA <- airportsAndCities.get(locationA) match { + case Some(location) => Success(location) + case None => Failure(GeoBaseException("Unknown location \"" + locationA + "\"")) + } + locationDetailsB <- airportsAndCities.get(locationB) match { + case Some(location) => Success(location) + case None => Failure(GeoBaseException("Unknown location \"" + locationB + "\"")) + } - case (Success(latA), Success(lngA), Success(latB), Success(lngB)) => - Success(round( - 2 * 6371 * asin(sqrt( - pow(sin(0.5 * (latA - latB)), 2) + - pow(sin(0.5 * (lngA - lngB)), 2) * cos(latA) * cos(latB) - )) - ).toInt) + latA <- locationDetailsA.getLatitude() + lngA <- locationDetailsA.getLongitude() + latB <- locationDetailsB.getLatitude() + lngB <- locationDetailsB.getLongitude() - case _ => { - if (latA.isFailure || lngA.isFailure) - Failure(GeoBaseException("No coordinates available for location \"" + locationA + "\"")) - else - Failure(GeoBaseException("No coordinates available for location \"" + locationB + "\"")) - } - } - } - } - } + } yield round( + 2 * 6371 * asin(sqrt( + pow(sin(0.5 * (latA - latB)), 2) + + pow(sin(0.5 * (lngA - lngB)), 2) * cos(latA) * cos(latB) + )) + ).toInt /** Transforms a local date (at a given location) into a GMT date. * @@ -335,10 +281,8 @@ class GeoBase() extends Serializable { * * {{{ * assert(geoBase.localDateToGMT("20160606_2227", "NYC") == Success("20160607_0227")) - * assert(geoBase.localDateToGMT("20160606_2227", "NYC").get == "20160607_0227") - * assert(geoBase.localDateToGMT("2016-06-06T22:27", "NYC", "yyyy-MM-dd'T'HH:mm").get == "2016-06-07T02:27") + * assert(geoBase.localDateToGMT("2016-06-06T22:27", "NYC", "yyyy-MM-dd'T'HH:mm") == Success("2016-06-07T02:27")) * assert(geoBase.localDateToGMT("20160606_2227", "~#?") == Failure(GeoBaseException: Unknown location "~#?")) - * assert(geoBase.localDateToGMT("20160606_2227", "~#?").getOrElse("20000101_1200") == "20000101_1200") * }}} * * @param localDate the local date at the given location under the given @@ -376,12 +320,10 @@ class GeoBase() extends Serializable { * * {{{ * assert(geoBase.getOffsetForLocalDate("20170712", "NCE") == Success(120)) - * assert(geoBase.getOffsetForLocalDate("20170712", "NCE").get == 120) - * assert(geoBase.getOffsetForLocalDate("2017-07-12", "NCE", "yyyy-MM-dd").get == 120) - * assert(geoBase.getOffsetForLocalDate("20171224", "NCE").get == 60) - * assert(geoBase.getOffsetForLocalDate("20171224", "NYC").get == -300) + * assert(geoBase.getOffsetForLocalDate("2017-07-12", "NCE", "yyyy-MM-dd") == Success(120)) + * assert(geoBase.getOffsetForLocalDate("20171224", "NCE") == Success(60)) + * assert(geoBase.getOffsetForLocalDate("20171224", "NYC") == Success(-300)) * assert(geoBase.getOffsetForLocalDate("20171224", "~#?") == Failure(GeoBaseException: Unknown location "~#?")) - * assert(geoBase.getOffsetForLocalDate("20171224", "~#?").getOrElse("0") == 0) * }}} * * @param localDate the local date @@ -393,20 +335,13 @@ class GeoBase() extends Serializable { */ def getOffsetForLocalDate( localDate: String, location: String, format: String = "yyyyMMdd" - ): Try[Int] = { + ): Try[Int] = for { - getTimeZone(location) match { + timeZone <- getTimeZone(location) - case Success(timeZone) => - Success( - TimeZone.getTimeZone(timeZone).getOffset( - new SimpleDateFormat(format).parse(localDate).getTime() - ) / 60000 - ) + dateTime = new SimpleDateFormat(format).parse(localDate).getTime() - case Failure(exception) => Failure(exception) - } - } + } yield TimeZone.getTimeZone(timeZone).getOffset(dateTime) / 60000 /** Transforms a GMT date into a local date for the given airport or city. * @@ -417,10 +352,8 @@ class GeoBase() extends Serializable { * * {{{ * assert(geoBase.gmtDateToLocal("20160606_1427", "NCE") == Success("20160606_1627")) - * assert(geoBase.gmtDateToLocal("20160606_1427", "NCE").get == "20160606_1627") - * assert(geoBase.gmtDateToLocal("2016-06-07T02:27", "NYC", "yyyy-MM-dd'T'HH:mm").get == "2016-06-06T22:27") + * assert(geoBase.gmtDateToLocal("2016-06-07T02:27", "NYC", "yyyy-MM-dd'T'HH:mm") == Success("2016-06-06T22:27")) * assert(geoBase.gmtDateToLocal("20160606_2227", "~#?") == Failure(GeoBaseException: Unknown location "~#?")) - * assert(geoBase.gmtDateToLocal("20160606_2227", "~#?").getOrElse("20000101_1200") == "20000101_1200") * }}} * * @param gmtDate the GMT date @@ -465,16 +398,14 @@ class GeoBase() extends Serializable { * * {{{ * assert(geoBase.getTripDurationFromLocalDates("20160606_1627", "CDG", "20160606_1757", "JFK") == Success(7.5d)) - * assert(geoBase.getTripDurationFromLocalDates("20160606_1627", "CDG", "20160606_1757", "JFK").get == 7.5d) * * val computedTripDuration = geoBase.getTripDurationFromLocalDates( * "2016-06-06T16:27", "CDG", "2016-06-06T17:57", "JFK", * format = "yyyy-MM-dd'T'HH:mm", unit = "minutes" * ) - * assert(computedTripDuration.get == 450d) + * assert(computedTripDuration == Success(450d)) * - * assert(geoBase.getTripDurationFromLocalDates("20160606_1627", "~#?", "20160606_1757", "JFK").get == Failure(GeoBaseException: Unknown location "~#?")) - * assert(geoBase.getTripDurationFromLocalDates("20160606_1627", "~#?", "20160606_1757", "JFK").getOrElse(-1d) == -1d) + * assert(geoBase.getTripDurationFromLocalDates("20160606_1627", "~#?", "20160606_1757", "JFK") == Failure(GeoBaseException: Unknown location "~#?")) * }}} * * @param localDepartureDate the departure local date @@ -499,27 +430,17 @@ class GeoBase() extends Serializable { "but not \"" + unit + "\"" ) - // We retrieve GMT dates in order to be able to do a real duration - // computation: - val gmtDepartureDate = localDateToGMT( - localDepartureDate, originLocation, format - ) - val gmtArrivalDate = localDateToGMT( - localArrivalDate, destinationLocation, format - ) - - (gmtDepartureDate, gmtArrivalDate) match { + for { - case (Failure(exception), _) => Failure(exception) + gmtDepDate <- localDateToGMT(localDepartureDate, originLocation, format) + gmtArrDate <- localDateToGMT(localArrivalDate, destinationLocation, format) - case (_, Failure(exception)) => Failure(exception) - - case (Success(gmtDepartureDate), Success(gmtArrivalDate)) => { + tripDuration <- { val formatter = new SimpleDateFormat(format) - val departureDate = formatter.parse(gmtDepartureDate) - val arrivalDate = formatter.parse(gmtArrivalDate) + val departureDate = formatter.parse(gmtDepDate) + val arrivalDate = formatter.parse(gmtArrDate) val tripDurationinMilliseconds = arrivalDate.getTime() - departureDate.getTime() @@ -527,22 +448,13 @@ class GeoBase() extends Serializable { if (tripDurationinMilliseconds < 0) Failure(GeoBaseException( "The trip duration computed is negative (maybe you've " + - "inverted departure/origin and arrival/destination)" - )) - - else { - - val tripDurationInMinutes = TimeUnit.MINUTES.convert( - tripDurationinMilliseconds, TimeUnit.MILLISECONDS - ) - - if (unit == "minutes") - Success(tripDurationInMinutes) - else - Success(tripDurationInMinutes / 60d) - } + "inverted departure/origin and arrival/destination)")) + else + Success(TimeUnit.MINUTES.convert( + tripDurationinMilliseconds, TimeUnit.MILLISECONDS)) } - } + + } yield if (unit == "minutes") tripDuration else tripDuration / 60d } /** Returns the geo type of a trip (domestic, continental or inter continental). @@ -555,12 +467,10 @@ class GeoBase() extends Serializable { * * {{{ * assert(geoBase.getGeoType(List("CDG", "ORY")) == Success(GeoType.DOMESTIC)) - * assert(geoBase.getGeoType(List("CDG", "ORY")).get == GeoType.DOMESTIC) - * assert(geoBase.getGeoType(List("FR", "FR")).get == GeoType.DOMESTIC) - * assert(geoBase.getGeoType(List("FR", "PAR", "DUB")).get == GeoType.CONTINENTAL) - * assert(geoBase.getGeoType(List("CDG", "TLS", "JFK", "MEX")).get == GeoType.INTER_CONTINENTAL) - * assert(geoBase.getGeoType(List("US", "bbb", "NCE", "aaa")).get == Failure(GeoBaseException: Unknown locations \"bbb\", \"aaa\")) - * assert(geoBase.getGeoType(List("US", "bbb", "NCE", "aaa")).toOption == None) + * assert(geoBase.getGeoType(List("FR", "FR")) == Success(GeoType.DOMESTIC)) + * assert(geoBase.getGeoType(List("FR", "PAR", "DUB")) == Success(GeoType.CONTINENTAL)) + * assert(geoBase.getGeoType(List("CDG", "TLS", "JFK", "MEX")) == Success(GeoType.INTER_CONTINENTAL)) + * assert(geoBase.getGeoType(List("US", "bbb", "NCE", "aaa")) == Failure(GeoBaseException: Unknown locations \"bbb\", \"aaa\")) * }}} * * @param locations a list of cities/ariports/countries representing the @@ -575,60 +485,29 @@ class GeoBase() extends Serializable { "at least 2 locations are needed to compute a geography type" ) - // We transform all locations in countries: - val distinctCountries = locations.map( - item => getCountryFor(item) - ).distinct + for { - // If at least one mapping airport/city to country is failing: - if (distinctCountries.exists(_.isFailure)) { + countries <- locations.traverse(location => getCountryFor(location)) + distCountries = countries.distinct - val invalidLocations = locations.filter( - item => getCountryFor(item).isFailure - ).map( - item => "\"" + item + "\"" - ) - - val plural = invalidLocations.size match { case 1 => "" case _ => "s" } - Failure(GeoBaseException("Unknown location" + plural + " " + invalidLocations.mkString(", "))) - } - - else { - - distinctCountries.length match { + geoType <- distCountries.length match { case 1 => Success(GeoType.DOMESTIC) - case _ => { + case _ => for { - // If it's not domestic, we transform all countries in iata - // zones: - val distinctIataZones = distinctCountries.flatMap(_.toOption).map( - country => getIataZoneFor(country) - ).distinct + iataZones <- distCountries.traverse(country => getIataZoneFor(country)) + distIataZones = iataZones.distinct - // If at least one mapping country to iata zone is failing: - if (distinctIataZones.exists(_.isFailure)) { - - val invalidCountries = - distinctCountries.flatMap(_.toOption).filter( - country => getIataZoneFor(country).isFailure - ).map( - country => "\"" + country + "\"" - ) - - val plural = invalidCountries.size match { case 1 => "y" case _ => "ies" } - Failure(GeoBaseException("Unknown countr" + plural + " " + invalidCountries.mkString(", "))) + geoType = distIataZones.length match { + case 1 => GeoType.CONTINENTAL + case _ => GeoType.INTER_CONTINENTAL } - else - distinctIataZones.length match { - case 1 => Success(GeoType.CONTINENTAL) - case _ => Success(GeoType.INTER_CONTINENTAL) - } - } + } yield geoType } - } + + } yield geoType } /** Returns the list of nearby airports (within the radius) for the given airport or city. @@ -638,9 +517,8 @@ class GeoBase() extends Serializable { * * {{{ * assert(geoBase.getNearbyAirportsWithDetails("CDG", 50) == Success(List("LBG", "ORY", "VIY", "POX"))) - * assert(geoBase.getNearbyAirportsWithDetails("CDG", 36).get == List("LBG", "ORY")) - * assert(geoBase.getNearbyAirportsWithDetails("~#?", 36)).get == Failure(GeoBaseException: Unknown location \"~#?\"")) - * assert(geoBase.getNearbyAirportsWithDetails("~#?", 36)).getOrElse(Nil) == List()) + * assert(geoBase.getNearbyAirportsWithDetails("CDG", 36) == Success(List("LBG", "ORY"))) + * assert(geoBase.getNearbyAirportsWithDetails("~#?", 36)) == Failure(GeoBaseException: Unknown location \"~#?\"")) * }}} * * @param location the airport or city for which to find nearby airports. @@ -648,13 +526,9 @@ class GeoBase() extends Serializable { * considered close. * @return the sorted per incresaing distance list nearby airports */ - def getNearbyAirports(location: String, radius: Int): Try[List[String]] = { - - getNearbyAirportsWithDetails(location, radius) match { - case Success(nearbyAirports) => Success(nearbyAirports.map(_._1)) - case Failure(exception) => Failure(exception) - } - } + def getNearbyAirports(location: String, radius: Int): Try[List[String]] = for { + nearbyAirports <- getNearbyAirportsWithDetails(location, radius) + } yield nearbyAirports.map(_._1) /** Returns the list of nearby airports (within the radius) for the given airport or city. * @@ -664,9 +538,8 @@ class GeoBase() extends Serializable { * * {{{ * assert(geoBase.getNearbyAirportsWithDetails("CDG", 50) == Success(List(("LBG", 9), ("ORY", 35), ("VIY", 37), ("POX", 38)))) - * assert(geoBase.getNearbyAirportsWithDetails("CDG", 36).get == List(("LBG", 9), ("ORY", 35))) - * assert(geoBase.getNearbyAirportsWithDetails("~#?", 36)).get == Failure(GeoBaseException: Unknown location \"~#?\"")) - * assert(geoBase.getNearbyAirportsWithDetails("~#?", 36)).getOrElse(Nil) == List()) + * assert(geoBase.getNearbyAirportsWithDetails("CDG", 36) == Success(List(("LBG", 9), ("ORY", 35)))) + * assert(geoBase.getNearbyAirportsWithDetails("~#?", 36)) == Failure(GeoBaseException: Unknown location \"~#?\"")) * }}} * * @param location the airport or city for which to find nearby @@ -683,28 +556,18 @@ class GeoBase() extends Serializable { // We check whether the given location is known: if (airportsAndCities.contains(location)) { - val nearbyAirports = airportsAndCities.keys.toList.filter( - // We only keep airport locations: - randomLocation => airportsAndCities(randomLocation).isAirport() - ).flatMap( - // We compute the distance between all airports to the given airport - // and we only keep those for which the distance is within the given - // radius: - randomAirport => { - - val distance = getDistanceBetween(location, randomAirport).getOrElse(-1) - - if (distance > 0 && distance <= radius) - Some((randomAirport, distance)) - else - None - } - ).sortWith( - // And we sort per increasing radius: - _._2 < _._2 - ) - - Success(nearbyAirports) + val nearbyAirports = for { + + randomLocation <- airportsAndCities.keys.toList + if (airportsAndCities(randomLocation).isAirport()) + + distance = getDistanceBetween(location, randomLocation).getOrElse(-1) + if (distance > 0) + if (distance <= radius) + + } yield (randomLocation, distance) + + Success(nearbyAirports.sortWith(_._2 < _._2)) // Sorted per increasing radius } else diff --git a/src/main/scala/com/geobase/load/Loader.scala b/src/main/scala/com/geobase/load/Loader.scala index 990c797..2a7cb2f 100644 --- a/src/main/scala/com/geobase/load/Loader.scala +++ b/src/main/scala/com/geobase/load/Loader.scala @@ -56,8 +56,7 @@ private[geobase] object Loader { val location = locations.map{ case (airportOrCityCode, location) => location }.foldLeft(locations.head._2) { - (locationA, locationB) => - if (locationA.isAirport()) locationA else locationB + (locA, locB) => if (locA.isAirport()) locA else locB } (airportOrCityCode, location) diff --git a/src/main/scala/com/geobase/model/Airline.scala b/src/main/scala/com/geobase/model/Airline.scala index a68030f..acd53cc 100644 --- a/src/main/scala/com/geobase/model/Airline.scala +++ b/src/main/scala/com/geobase/model/Airline.scala @@ -11,12 +11,9 @@ import scala.util.{Try, Success, Failure} */ private[geobase] final case class Airline(airlineCode: String, countryCode: String) { - def getCountry(): Try[String] = { - countryCode match { - case "" => Failure(GeoBaseException( - "No country available for airline \"" + airlineCode + "\"" - )) - case _ => Success(countryCode) - } + def getCountry(): Try[String] = countryCode match { + case "" => Failure(GeoBaseException( + "No country available for airline \"" + airlineCode + "\"")) + case _ => Success(countryCode) } } diff --git a/src/main/scala/com/geobase/model/AirportOrCity.scala b/src/main/scala/com/geobase/model/AirportOrCity.scala index ecf865b..45cebc2 100644 --- a/src/main/scala/com/geobase/model/AirportOrCity.scala +++ b/src/main/scala/com/geobase/model/AirportOrCity.scala @@ -32,57 +32,44 @@ private[geobase] final case class AirportOrCity( def isAirport(): Boolean = locationType == "A" - def getCity(): Try[String] = { - getCities() match { - case Success(cities) => Success(cities.head) - case Failure(exception) => Failure(exception) - } + def getCity(): Try[String] = getCities() match { + case Success(city :: _) => Success(city) + case Failure(exception) => Failure(exception) } - def getCities(): Try[List[String]] = { + def getCities(): Try[List[String]] = cityCode.length match { - if (cityCode.length == 3) - Success(List(cityCode)) + case 3 => Success(List(cityCode)) - // In case of an airport attached to several cities ("PHX,MSC"): - else if (cityCode.length >= 3) - cityCode.split("\\,", -1).toList.filter(_.length == 3) match { - case Nil => Failure(GeoBaseException("No city available for airport \"" + iataCode + "\"")) - case cities => Success(cities) - } + case x if x >= 3 => cityCode.split("\\,", -1).toList.filter(_.length == 3) match { + case Nil => Failure(GeoBaseException("No city available for airport \"" + iataCode + "\"")) + case cities => Success(cities) + } - // Raws for which the city field is empty: - else - Failure(GeoBaseException("No city available for airport \"" + iataCode + "\"")) + case _ => Failure(GeoBaseException("No city available for airport \"" + iataCode + "\"")) } - def getCountry(): Try[String] = { - countryCode match { - case "" => Failure(GeoBaseException( - "No country available for location \"" + iataCode + "\"" - )) - case _ => Success(countryCode) - } + def getCountry(): Try[String] = countryCode match { + case "" => Failure(GeoBaseException( + "No country available for location \"" + iataCode + "\"")) + case _ => Success(countryCode) } - def getTimeZone(): Try[String] = { - timeZone match { - case "" => Failure(GeoBaseException( - "No time zone available for location \"" + iataCode + "\"" - )) - case _ => Success(timeZone) - } + def getTimeZone(): Try[String] = timeZone match { + case "" => Failure(GeoBaseException( + "No time zone available for location \"" + iataCode + "\"")) + case _ => Success(timeZone) } - /** Returns the longitude. - * - * The raw longitude field might be empty and thus not castable. - */ - def getLongitude(): Try[Double] = Try(longitude.toDouble / 180 * Pi) + def getLongitude(): Try[Double] = Try(longitude.toDouble) match { + case Success(longitude) => Success(longitude / 180d * Pi) + case Failure(_) => Failure(GeoBaseException( + "No longitude available for location \"" + iataCode + "\"")) + } - /** Returns the latitude. - * - * The raw latitude field might be empty and thus not castable. - */ - def getLatitude(): Try[Double] = Try(latitude.toDouble / 180 * Pi) + def getLatitude(): Try[Double] = Try(latitude.toDouble) match { + case Success(latitude) => Success(latitude / 180d * Pi) + case Failure(_) => Failure(GeoBaseException( + "No latitude available for location \"" + iataCode + "\"")) + } } diff --git a/src/main/scala/com/geobase/model/Country.scala b/src/main/scala/com/geobase/model/Country.scala index 63a4fe4..d41dc36 100644 --- a/src/main/scala/com/geobase/model/Country.scala +++ b/src/main/scala/com/geobase/model/Country.scala @@ -13,30 +13,21 @@ private[geobase] final case class Country( countryCode: String, currencyCode: String, continentCode: String, iataZone: String ) { - def getContinent(): Try[String] = { - continentCode match { - case "" => Failure(GeoBaseException( - "No continent available for country \"" + countryCode + "\"" - )) - case _ => Success(continentCode) - } + def getContinent(): Try[String] = continentCode match { + case "" => Failure(GeoBaseException( + "No continent available for country \"" + countryCode + "\"")) + case _ => Success(continentCode) } - def getIataZone(): Try[String] = { - iataZone match { - case "" => Failure(GeoBaseException( - "No iata zone available for country \"" + countryCode + "\"" - )) - case _ => Success(iataZone) // Non-empty string - } + def getIataZone(): Try[String] = iataZone match { + case "" => Failure(GeoBaseException( + "No iata zone available for country \"" + countryCode + "\"")) + case _ => Success(iataZone) } - def getCurrency(): Try[String] = { - currencyCode match { - case "" => Failure(GeoBaseException( - "No currency available for country \"" + countryCode + "\"" - )) - case _ => Success(currencyCode) // Non-empty string - } + def getCurrency(): Try[String] = currencyCode match { + case "" => Failure(GeoBaseException( + "No currency available for country \"" + countryCode + "\"")) + case _ => Success(currencyCode) } } diff --git a/src/test/scala/com/geobase/GeoBaseTest.scala b/src/test/scala/com/geobase/GeoBaseTest.scala index 9e997a0..d05b926 100644 --- a/src/test/scala/com/geobase/GeoBaseTest.scala +++ b/src/test/scala/com/geobase/GeoBaseTest.scala @@ -217,7 +217,7 @@ class GeoBaseTest extends FunSuite with PrivateMethodTester { exceptionThrown = intercept[GeoBaseException] { geoBase.getGeoType(List("US", "bbb", "NCE", "aaa")).get } - assert(exceptionThrown.getMessage === "Unknown locations \"bbb\", \"aaa\"") + assert(exceptionThrown.getMessage === "Unknown location \"bbb\"") // Unknown IATA zone for a country: exceptionThrown = intercept[GeoBaseException] { @@ -227,7 +227,7 @@ class GeoBaseTest extends FunSuite with PrivateMethodTester { exceptionThrown = intercept[GeoBaseException] { geoBase.getGeoType(List("FR", "XX", "..")).get } - assert(exceptionThrown.getMessage === "Unknown countries \"XX\", \"..\"") + assert(exceptionThrown.getMessage === "Unknown country \"XX\"") } test("Local Date to GMT Date") {