All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.taig.taigless.geo.GoogleMapsGeo.scala Maven / Gradle / Ivy

There is a newer version: 0.15.0
Show newest version
package io.taig.taigless.geo

import cats.effect.kernel.{Async, Resource}
import cats.syntax.all._
import com.google.maps._
import com.google.maps.errors.InvalidRequestException
import com.google.maps.model.{Unit => _, _}

import java.net.URL
import java.time.ZoneId
import java.util.TimeZone
import scala.util.chaining._

final class GoogleMapsGeo[F[_]](context: GeoApiContext)(implicit F: Async[F]) extends Geo[F, GoogleMapsGeo.PlaceId] {
  override def find(query: String, locale: Option[String]): F[List[Geo.Location[GoogleMapsGeo.PlaceId]]] =
    F.async_[Array[GeocodingResult]] { callback =>
      GeocodingApi
        .geocode(context, query)
        .pipe { request =>
          locale.fold(request)(request.language)
        }
        .components(ComponentFilter.country("de"))
        .resultType(AddressType.STREET_ADDRESS)
        .setCallback(new PendingResult.Callback[Array[GeocodingResult]] {
          override def onResult(result: Array[GeocodingResult]): Unit = callback(Right(result))

          override def onFailure(throwable: Throwable): Unit = callback(Left(throwable))

        })
    }.map { results =>
      results.toList.map { result =>
        val formatted = result.formattedAddress
        val position = toPosition(result.geometry.location)
        val url = new URL(GoogleMaps.locationUrl(formatted, position))
        Geo.Location(GoogleMapsGeo.PlaceId(result.placeId), formatted, position, url)
      }
    }

  override def findByIdentifier(
      identifier: GoogleMapsGeo.PlaceId,
      locale: Option[String]
  ): F[Option[Geo.Location[GoogleMapsGeo.PlaceId]]] =
    F.async_[Option[PlaceDetails]] { callback =>
      PlacesApi
        .placeDetails(context, identifier.value)
        .pipe(request => locale.fold(request)(request.language))
        .setCallback(new PendingResult.Callback[PlaceDetails] {
          override def onResult(result: PlaceDetails): Unit = callback(Right(Some(result)))

          override def onFailure(throwable: Throwable): Unit = callback(Left(throwable))
        })
    }.recover { case _: InvalidRequestException => None }
      .map {
        case Some(details) =>
          Some(Geo.Location(identifier, details.formattedAddress, toPosition(details.geometry.location), details.url))
        case None => None
      }

  override def timezone(position: Position): F[ZoneId] =
    F.async_[TimeZone] { callback =>
      TimeZoneApi
        .getTimeZone(context, toLatLng(position))
        .setCallback(new PendingResult.Callback[TimeZone] {
          override def onResult(result: TimeZone): Unit = callback(Right(result))

          override def onFailure(throwable: Throwable): Unit = callback(Left(throwable))
        })
    }.map(_.toZoneId)

  def toLatLng(position: Position): LatLng = new LatLng(position.latitude.value, position.longitude.value)

  def toPosition(latLng: LatLng): Position = Position(Latitude(normalize(latLng.lat)), Longitude(normalize(latLng.lng)))

  def normalize(axis: Double): Double =
    BigDecimal.valueOf(axis).setScale(7, BigDecimal.RoundingMode.HALF_UP).doubleValue
}

object GoogleMapsGeo {
  final case class PlaceId(value: String) extends AnyVal

  def apply[F[_]: Async](context: GeoApiContext): Geo[F, PlaceId] = new GoogleMapsGeo[F](context)

  def fromApiKey[F[_]](key: String)(implicit F: Async[F]): Resource[F, Geo[F, PlaceId]] = {
    val acquire = F.blocking(new GeoApiContext.Builder().apiKey(key).build())
    Resource.make(acquire)(context => F.blocking(context.shutdown())).map(GoogleMapsGeo[F])
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy