Interface

Dane pozyskiwane były za pomocą interfejsu REST-API oraz w jednym przypadku, używając dedykowanej biblioteki. W przypadku danych wymagających zarządzania limitem przypisanym do indywidualnego token’a, proces ten odbywał się w środowisku R. Dla danych z serwisów niewymagających autoryzacji - środowisko Power Query.

Turystyka GOV - Power Query

W przypadku środowiska Microsoft, problem ten sprowadzał się do utworzenia zapytania w języku M oraz dalszych przekształceń z użyciem interfejsu graficznego Power Query

= Json.Document(
	Web.Contents("
		https://api.turystyka.gov.pl/registers/open/cwoh" &
		"?city=Kraków&size=300",
		[Headers=[accept="application/json
	"]]))

Odpowiedzią na powyższe żądanie są dane pochodzące z serwisu turystyka.gov.pl, w formacie JSON. Zapytanie zostało wykonane dla miasta Kraków (obszar analizy) oraz limitem 300 hoteli.

GooglePlaces - R

W związku z tym, że pomiędzy danymi z Google Places, a Turystyka GOV nie ma wspólnego, jednoznacznego identyfikatora, posłużono się trzema wartościami - kodem pocztowym, nazwą ulicy oraz nazwą miejsca. Następnie posłużono się silnikiem Google Find Place do zawężenia poszukiwań, a ostateczną lokację wybrano z zastosowaniem dodatkowych algorytmów podobieństwa tekstowego.

### Function to fetch Google Places data based on name and address
fetch_google_place_id <- function(hotel_name, street, postal_code) {
  # Google Places API request
  places_url <- "https://maps.googleapis.com/maps/api/place/findplacefromtext/json"
  query <- paste(hotel_name, street, postal_code)
  
  params <- list(
    input = query,
    inputtype = "textquery",
    fields = "place_id,name,formatted_address",
    key = config::get("google_api_key")
  )
  
  response <- GET(places_url, query = params, add_headers(accept = "application/json"))
  
  if (status_code(response) == 200) {
    data <- fromJSON(content(response, as = "text", encoding = "UTF-8"))
    
    if (length(data$candidates) > 0) {
      matches <- data$candidates |> 
        as.data.frame(stringsAsFactors = FALSE) |>
        mutate(
          name_similarity = stringdist::stringdist(
            tolower(hotel_name), 
            tolower(name), 
            method = "jw"
          ),
          street_match = grepl(
            gsub("\\s+", " ", trimws(street)), 
            gsub("\\s+", " ", trimws(formatted_address)), 
            ignore.case = TRUE
          ),
          postal_code_match = grepl(
            gsub("\\s+", " ", trimws(postal_code)), 
            gsub("\\s+", " ", trimws(formatted_address)), 
            ignore.case = TRUE
          ),
          score = (1 - name_similarity) * 0.6 + 
            street_match * 0.2 + 
            postal_code_match * 0.2
        ) |> 
        arrange(desc(score)) |> 
        head(1)  # Pick the best match
      
      return(matches$place_id[1])
    }
  }
  return(NA)
}

Po dopasowaniu ID z GooglePlaces do Turystyka GOV, możliwe było dalsze importowanie danych. Kody zapytań REST API poniżej.

############ GOOGLE REVIEW AND RATINGS
### Fetch Google reviews and ratings
base_url <- "https://maps.googleapis.com/maps/api/place/details/json"
 
results <- lapply(dfGooglePlace$id_google, function(place_id) {
  params <- list(
    place_id = place_id,
    fields = "name,rating,reviews,user_ratings_total,photos,price_level",
    key = config::get("google_api_key")
  )
  
  response <- GET(base_url, query = params, add_headers(accept = "application/json"))
  Sys.sleep(2)  # Small delay to allow token to activate
  
  if (status_code(response) == 200) {
    data <- fromJSON(content(response, as = "text", encoding = "UTF-8"))
    # Extract key information with place ID
    place_info <- tryCatch({
      data$result %>%
        as.data.frame(stringsAsFactors = FALSE) %>%
        select(any_of(c(
          "name",
          "user_ratings_total", 
          "rating", 
          "reviews.author_name", 
          "reviews.rating", 
          "reviews.text", 
          "relative_time_description", 
          "reviews.profile_photo_url"
        ))) %>%
        mutate(id_google = place_id)
    }, error = function(e) {
      data.frame(id_google = place_id)
    })
    
    # Handle reviews with place ID
    reviews_df <- tryCatch({
      if (!is.null(data$result$reviews)) {
        data$result$reviews %>%
          as.data.frame(stringsAsFactors = FALSE) %>%
          select(any_of(c("author_name", "rating", "text", "time"))) %>%
          mutate(id_google = place_id)
      } else {
        data.frame(id_google = place_id)
      }
    }, error = function(e) {
      data.frame(id_google = place_id)
    })
    
    list(place_info = place_info, reviews = reviews_df)
    
  } else {
    print(paste("Error:", status_code(response), "in place_id:", place_id))
    list(place_info = data.frame(id_google = place_id), reviews = data.frame(id_google = place_id))
  }
})

Dodatkowo, pobrano informacje o najczęściej odwiedzanych obiektach turystycznych…

####### FIND MOST POPULAR PLACES
### Function to fetch paginated results
# API base URL and parameters
base_url <- "https://maps.googleapis.com/maps/api/place/nearbysearch/json"
latitude <- 50.0619
longitude <- 19.9368
radius <- 10000  # Radius in meters
 
get_places <- function(location, radius, type, api_key) {
  all_results <- list()
  next_page_token <- NULL
  
  repeat {
    params <- list(
      location = location,
      radius = radius,
      type = type,
      key = api_key
    )
    
    # Add next_page_token if available
    if (!is.null(next_page_token)) {
      params$pagetoken <- next_page_token
      Sys.sleep(2)  # Recommended delay for next request
    }
    
    # API request
    response <- GET(base_url, query = params)
    data <- fromJSON(content(response, "text"), flatten = TRUE)
    
    # Append results
    all_results <- append(all_results, list(data$results))
    
    # Check for next page
    if (!is.null(data$next_page_token)) {
      next_page_token <- data$next_page_token
    } else {
      break
    }
  }

Open Street Map - R

Do zebrania informacji o drogach posłużył pakiet osmdata umożliwiający sprawne korzystanie z API.

pacman::p_load(
  sf,
  osmdata,
  dplyr
)
 
# Download major road data for Kraków from OpenStreetMap
road_data <- opq(bbox = "Kraków, Poland") %>%
  add_osm_feature(key = "highway", 
                  value = c("motorway", "trunk", "primary", "secondary", "tertiary")) %>%
  osmdata_sf()
 
# Extract line geometries representing roads
roads <- road_data$osm_lines
 
# Convert road lines into points (vertices) for distance calculations
road_points <- st_cast(roads, "POINT")
 
# Extract coordinates and other relevant information
road_points_df <- road_points %>%
  mutate(
    longitude = st_coordinates(geometry)[, 1],
    latitude = st_coordinates(geometry)[, 2]
  ) %>%
  st_drop_geometry() %>%  
  select(osm_id, name, highway, longitude, latitude)
 
readr:::write_csv(road_points_df, "./data/OSM-Road.csv")