Getting and cleaning data, example of Chinese airports - Part 4/5

By Datapleth.io | January 1, 2016

One of the big problem for anybody interested in China and data science is the availability of data sets. There are limited free resources available and they are often incomplete or inaccurate. Getting data and especially cleaning data becomes one of the biggest pain of data science applied to China.

The objective of this series of post is to illustrate the problem and associated process on a specific example: plot a map of the airports of mainland China.

In this forth part, we merge the two data obtained in part 2 and part 3 sets by IATA.FAA code and will clean / fix missing data. We will create the separate data set with the complement which will be fixed in next part.

In order to compare and fix missing or wrong data we are going to use different tools :

  • stringdist from the package of the same name to check the distance between two chains of characters
  • the Google map API which give back elevation for a set of longitude / latitude

Dependencies

We will need few standard packages as loaded bellow.

library(maptools)
library(reshape2)
library(grid)
library(dplyr)
library(ggplot2)
library(jsonlite)
library(Amelia)
library(OpenStreetMap)
library(stringdist)
library(grid)
library(knitr)
## For satellite image analysis
library(slippymath)
library(glue)
library(raster)
library(png)
library(purrr)
library(curl)
# get API from system environment
api_mapbox <- Sys.getenv("API_MAPBOX")
api_elevation <- Sys.getenv("API_GOOG_ELEVATION")

Loading part 3 objects

load(file = "./data/chinese-airports-part-3.Rda")

Merging the two datasets by IATA.FAA code and extracting complements

We will use IATA.FAA codes to identify the airports. First let’s create subset of airport with and without IATA codes.

OAwoIATA <- airportChinaOurairports[is.na(airportChinaOurairports$IATA.FAA),]
OAwithIATA <- airportChinaOurairports[!is.na(airportChinaOurairports$IATA.FAA),]
OFwoIATA <- airportChinaOpenflights[is.na(airportChinaOpenflights$IATA.FAA),]
OFwithIATA <- airportChinaOpenflights[!is.na(airportChinaOpenflights$IATA.FAA),]

Let’s merge the two sub data sets with IATA code present. Then we isolate the remaining airport which have no match. Regroup with airport without IATA and check if there are possible match by ICAO codes.

## Merge by IATA, drop non matchin case
IATA <- merge(OAwithIATA, OFwithIATA, by = "IATA.FAA")
nrow(IATA)
## [1] 179
## Extract OA airports not matching IATA code from OF
## And regroup with Airports without IATA
OAout <- OAwithIATA[!(OAwithIATA$IATA.FAA %in% IATA$IATA.FAA),]
OAout <- rbind(OAout, OAwoIATA)
## Extract OF airports not matching IATA code from OA
OFout <- OFwithIATA[!(OFwithIATA$IATA.FAA %in% IATA$IATA.FAA),]
OFout <- rbind(OFout, OFwoIATA)
## Check if there could be match by ICAO codes when present
sum(OAout[!is.na(OAout$ICAO),] %in% OFout[!is.na(OFout$ICAO),])
## [1] 0

We have now three data sets to work on :

  • IATA which contains common airports by IATA code between openflight and ourairport data set. We need to define a strategy to select or merge their parameters if data is different (which elevation choose when different, which name, etc…)
  • OFout airport from Openflight which have no match by IATA nor ICAO in ourairport data or no IATA/ICAO code.
  • OAout airport from ourairport which have no match by IATA nor ICAO in openflights data or no IATA/ICAO code

For the two last data set, we merge them and we will have to find a way to collect missing data and add these to IATA data set in the later part of this report.

# add missing categories to OFout
OFout$type.airport <- NA
OFout$scheduled.service <- NA
OFout$airport.wikipedia.link <- NA
OFout$region.name <- NA      
airports_2_check <- rbind(OFout, OAout)

missmap(IATA, main = "Missingness map for merged airport list by IATA.FAA Code")

missmap(airports_2_check, main = "Missingness map for remaining airports to be checked (not in both OA / OF sets")

Checking and fixing IATA dataset

Reorganising columns

We update column order to get a clearer view when comparing data.

# reorder
IATA <- dplyr::select(IATA,
                      IATA.FAA, ICAO.x, ICAO.y,
                      name.x, name.y, city.x, city.y,
                      latitude.deg.x, latitude.deg.y,
                      longitude.deg.x, longitude.deg.y,
                      elevation.m.x, elevation.m.y,
                      category.x, category.y,
                      region.name, type.airport, scheduled.service, airport.wikipedia.link
) 

Fixing ICAO codes

Let’s clean the ICAO codes, we will use the one from OurAiports. All missing values of OpenFlight are included in Ouraiports. Missing values of OurAirports are not found in Openflight data set (see previous missingness map)

IATA$ICAO <- IATA$ICAO.x
IATA <- dplyr::select(IATA, -ICAO.x, -ICAO.y)
missmap(IATA)

Fixing city name

No missing data in city.y, we replace missing city.x by associated city.y. There seems to be small typos in both data set (always for Chinese cities), we compare city names by calculating the distance between the character vectors. We keep city.x which seems better.

IATA[is.na(IATA$city.x),]$city.x <- IATA[is.na(IATA$city.x),]$city.y 

dist <- stringdist(IATA$city.x, IATA$city.y)
hist(dist, breaks = 40)

kable(dplyr::select(IATA[dist > 0,], city.x, city.y, name.x))
city.x city.y name.x
21 Changsha Changcha Changsha Huanghua International Airport
26 Daocheng County Daocheng Daocheng Yading Airport
30 Xiaguan Dali Dali Airport
33 Ordos Dongsheng Ordos Ejin Horo Airport
41 Xiahe Xiahe city Gannan Xiahe Airport
50 Huai’an Huai An Lianshui Airport
51 Huaihua Zhijiang Zhijiang Airport
66 Jiagedaqi Jiagedaqi District Jiagedaqi Airport
68 Ji’an Ji An Jinggangshan Airport
81 Kashgar Kashi Kashgar Airport
89 Guilin City Guilin Guilin Liangjiang International Airport
94 Lüliang Lvliang Lüliang Airport
106 Meixian Xi’an Meixian Airport
111 Ningbo Ninbo Ningbo Lishe International Airport
114 Xinyuan County Xi’an Xinyuan Nalati Airport
119 Mohe Mohe County Gu-Lian Airport
123 Xigazê Shigatse Shigatse Air Base
124 Bavannur Bayannur Bayannur Tianjitai Airport
130 Pu’er Simao Pu’er Simao Airport
145 Ürümqi Urumqi Ürümqi Diwopu International Airport
156 Wanxian Xi’an Wanxian Airport
157 Xiangfan Xi’an Xiangyang Liuji Airport
158 Xichang Xi’an Xichang Qingshan Airport
173 Yangzhou and Taizhou Yangzhou Yangzhou Taizhou Airport
IATA$city <- IATA$city.x
IATA <- dplyr::select(IATA, -city.x, -city.y)
missmap(IATA)

Fixing airport names

No missing data in name.y or name.x, we need to compare both by string distance. name.x is more detailed and better written. Except for Taoxian Airport.

dist <- stringdist(IATA$name.x, IATA$name.y)
hist(dist, breaks = 40)

kable(head(dplyr::select(IATA[dist > 0,], name.x, name.y)))
name.x name.y
1 Altay Air Base Altay Airport
3 Baise Youjiang Airport Tianyang
4 Ankang Wulipu Airport Ankang Airport
6 Anqing Tianzhushan Airport Anqing Airport
11 Baoshan Yunduan Airport Baoshan Airport
12 Guangzhou Baiyun International Airport Baiyun Intl
IATA[IATA$name.x == "Taoxian Airport",]$name.x <- "Shenyang Taoxian International Airport"
IATA$name <- IATA$name.x
IATA <- dplyr::select(IATA, -name.x, -name.y)
missmap(IATA)

Fixing longitude.deg, latitude.deg

Values for longitude and latitude are different in the two data sets, we study in details the distance over 0.01 degree which represent roughly 500 meters.

dist1 <- abs(IATA$longitude.deg.x - IATA$longitude.deg.y)
dist2 <- abs(IATA$latitude.deg.x - IATA$latitude.deg.y)

par(mfrow = c(1,2))
hist(log(dist1), breaks = 40)
hist(log(dist2), breaks = 40)

toCheck <- dplyr::select(IATA[dist1 > 0.05 & dist2 > 0.05,],
                         IATA.FAA, ICAO, airport.wikipedia.link,
                         latitude.deg.x, longitude.deg.x,
                         latitude.deg.y,  longitude.deg.y)

kable(toCheck)
IATA.FAA ICAO airport.wikipedia.link latitude.deg.x longitude.deg.x latitude.deg.y longitude.deg.y
15 CGQ ZYCC https://en.wikipedia.org/wiki/Changchun_Longjia_International_Airport 43.99620 125.68500 43.54120 125.12010
25 DAX ZUDX https://en.wikipedia.org/wiki/Daxian_Airport 31.13020 107.42950 31.30000 107.50000
31 DNH ZLDH https://en.wikipedia.org/wiki/Dunhuang_Airport 40.16110 94.80920 40.09400 94.48180
32 DOY ZSDY https://en.wikipedia.org/wiki/Dongying_Shengli_Airport 37.50860 118.78800 37.27160 118.28190
33 DSN ZBDS https://en.wikipedia.org/wiki/Ordos_Ejin_Horo_Airport 39.49000 109.86139 39.85000 110.03300
39 FUO ZGFS https://en.wikipedia.org/wiki/Foshan_Shadi_Airport 23.08330 113.07000 23.13333 113.28333
40 GOQ ZLGM https://en.wikipedia.org/wiki/Golmud_Airport 36.40060 94.78610 34.63300 98.86700
41 GXH ZLXH https://en.wikipedia.org/wiki/Gannan_Xiahe_Airport 34.81050 102.64470 34.49090 102.37190
60 HZG ZLHZ https://en.wikipedia.org/wiki/Hanzhong_Chenggu_Airport 33.13414 107.20601 33.06360 107.00800
61 HZH ZUNP https://en.wikipedia.org/wiki/Liping_Airport 26.32217 109.14990 26.20600 109.03900
62 INC ZLIC https://en.wikipedia.org/wiki/Yinchuan_Hedong_International_Airport 38.32276 106.39321 38.48194 106.00917
63 IQM ZWCM https://en.wikipedia.org/wiki/Qiemo_Yudu_Airport 38.23361 85.46556 38.14940 85.53280
71 JIU ZSJJ https://en.wikipedia.org/wiki/Jiujiang_Lushan_Airport 29.47694 115.80111 29.73300 115.98300
74 JNG ZSJG https://en.wikipedia.org/wiki/Jining_Qufu_Airport 35.29278 116.34667 35.41700 116.53300
84 KMG ZPPP https://en.wikipedia.org/wiki/Kunming_Changshui_International_Airport 25.10194 102.92917 24.99236 102.74354
87 KRY ZWKM https://en.wikipedia.org/wiki/Karamay_Airport 45.46655 84.95270 45.61700 84.88300
98 LYA ZHLY https://en.wikipedia.org/wiki/Luoyang_Beijiao_Airport 34.74110 112.38800 34.41000 112.28000
126 SHE ZYTX https://en.wikipedia.org/wiki/Shenyang_Taoxian_International_Airport 41.63980 123.48300 41.38240 123.29010
129 SWA ZGOW https://en.wikipedia.org/wiki/Jieyang_Chaoshan_International_Airport 23.55200 116.50330 23.40000 116.68300
138 THQ ZLTS https://en.wikipedia.org/wiki/Tianshui_Maijishan_Airport 34.55940 105.86000 34.33300 105.51400
139 TLQ ZWTP https://en.wikipedia.org/wiki/Turpan_Jiaohe_Airport 43.03080 89.09870 42.94233 89.18588
146 UYN ZLYL https://en.wikipedia.org/wiki/Yulin_Yuyang_Airport 38.35971 109.59093 38.26920 109.73100
149 WNH ZPWS https://en.wikipedia.org/wiki/Wenshan_Puzhehei_Airport 23.55830 104.32550 23.37583 104.24306
163 XUZ ZSXZ https://en.wikipedia.org/wiki/Xuzhou_Guanyin_Airport 34.05906 117.55528 34.16000 117.11000
171 YNT ZSYT https://en.wikipedia.org/wiki/Yantai_Penglai_International_Airport 37.65722 120.98722 37.40167 121.37167

Let’s now plot these airports.

g <- ggplot() + theme_bw()
## airports with elevation
g <- g + geom_point(data = toCheck, aes(x = longitude.deg.x, y = latitude.deg.x), col = "firebrick", pch = 3)
## airport with 0 m  as elevation in red
g <- g + geom_point(data = toCheck, aes(x = longitude.deg.y, y = latitude.deg.y), col = "blue", pch = 1)
g <- g + geom_text(data = toCheck, aes(x = longitude.deg.x, y = latitude.deg.x, label=IATA.FAA), size = 3, vjust = -1)
print(g)

The map above shows there are significant error between the two data sets, now let’s sample one case to check which data set is probably the most accurate. We used for that the OpenStreetMap package to display side by side satellite images around the latitude and longitude. This worked but for some unknown reasons some tiles cannot be downloaded from travis. So I changed strategy, using the API of mapbox instead but with longer and more complicated code.

# set-up size of the bounding box to be visualized.
delta_lon <- 0.01
delta_lat <- 0.005
zoom_level <- 15


mapAirport <- function(
  my_lon, my_lat,
  my_delta_lat = 0.01,
  my_delta_lon = 0.005,
  my_zoom_level = 14,
  airport_id = 1,
  my_api_mapbox
) {
  ## Define bounding box
  myBbox <- c(xmin = my_lon - my_delta_lon,
              ymin = my_lat - my_delta_lat,
              xmax = my_lon + my_delta_lon,
              ymax = my_lat + my_delta_lat
  )
  # make the gridding
  myBboxTile <- slippymath::bbox_tile_query(bbox = myBbox)
  myGrid <- slippymath::bbox_to_tile_grid(myBbox, zoom = my_zoom_level)
  
  
  ## Download satellite data
  setwd(dir = "./sat/")
  
  mapbox_query_string <-
    paste0(
      "https://api.mapbox.com/v4/mapbox.satellite/{zoom}/{x}/{y}.jpg90",
      #"https://api.mapbox.com/v4/mapbox.satellite/{zoom}/{x}/{y}@2x.jpg90",
      "?access_token=",
      my_api_mapbox
    )
  
  
  
  ## Function to download Tiles
  myTiles <-
    pmap(.l = myGrid$tiles,
         zoom = myGrid$zoom,
         
         .f = function(x, y, zoom){
           outfile <- glue("{x}_{y}.jpg")
           curl_download(url = glue(mapbox_query_string),
                         destfile = outfile)
           outfile
         }
    )
  
  
  mySat <- slippymath::compose_tile_grid(myGrid, myTiles)
  #raster::plot(mySat)
  
  ## Go back to root directory
  setwd(dir = "../")
  #raster_to_png(mySat, "../../static/img/md/test_mapbox.png")
  raster_to_png(mySat, paste0("./img/airport_",airport_id,".png"))
  #knitr::include_graphics("/img/md/test_mapbox.png")
  #knitr::include_graphics(paste0("/blog/img/airport_",airport_id,".png"))
  return(paste0("/blog/img/airport_",airport_id,".png"))
  #overlay_image2 <- png::readPNG("final.png")
}
list_img <- NULL

for( i in 1:nrow(toCheck)){
  cat(paste0("<br/><br/><b>",toCheck[i,]$IATA.FAA,
             " - <a href='",toCheck[i,]$airport.wikipedia.link,"'>",
             toCheck[i,]$airport.wikipedia.link,"</a></b></br>\n"))
  a <- mapAirport(
    my_lon = toCheck[i,]$longitude.deg.x,
    my_lat = toCheck[i,]$latitude.deg.x,
    my_delta_lat = delta_lat,
    my_delta_lon = delta_lon,
    my_zoom_level = zoom_level,
    airport_id = 2*i,
    my_api_mapbox = api_mapbox
  )
  #cat(paste0("![",toCheck[i,]$IATA.FAA,"_OA](", a, " =300x)\n"))
  cat(paste0("<img src=",a," alt='",toCheck[i,]$IATA.FAA,"_OA'"," width='300'/>\n"))
  b <- mapAirport(
    my_lon = toCheck[i,]$longitude.deg.y,
    my_lat = toCheck[i,]$latitude.deg.y,
    my_delta_lat = delta_lat,
    my_delta_lon = delta_lon,
    my_zoom_level = zoom_level,
    airport_id = 2*i+1,
    my_api_mapbox = api_mapbox
  )
  #cat(paste0("![",toCheck[i,]$IATA.FAA,"_OF](", b, " =300x)\n"))
  cat(paste0("<img src=",b," alt='",toCheck[i,]$IATA.FAA,"_OF'"," width='300'/>\n"))
}



CGQ - https://en.wikipedia.org/wiki/Changchun_Longjia_International_Airport
CGQ_OA CGQ_OF

DAX - https://en.wikipedia.org/wiki/Daxian_Airport
DAX_OA DAX_OF

DNH - https://en.wikipedia.org/wiki/Dunhuang_Airport
DNH_OA DNH_OF

DOY - https://en.wikipedia.org/wiki/Dongying_Shengli_Airport
DOY_OA DOY_OF

DSN - https://en.wikipedia.org/wiki/Ordos_Ejin_Horo_Airport
DSN_OA DSN_OF

FUO - https://en.wikipedia.org/wiki/Foshan_Shadi_Airport
FUO_OA FUO_OF

GOQ - https://en.wikipedia.org/wiki/Golmud_Airport
GOQ_OA GOQ_OF

GXH - https://en.wikipedia.org/wiki/Gannan_Xiahe_Airport
GXH_OA GXH_OF

HZG - https://en.wikipedia.org/wiki/Hanzhong_Chenggu_Airport
HZG_OA HZG_OF

HZH - https://en.wikipedia.org/wiki/Liping_Airport
HZH_OA HZH_OF

INC - https://en.wikipedia.org/wiki/Yinchuan_Hedong_International_Airport
INC_OA INC_OF

IQM - https://en.wikipedia.org/wiki/Qiemo_Yudu_Airport
IQM_OA IQM_OF

JIU - https://en.wikipedia.org/wiki/Jiujiang_Lushan_Airport
JIU_OA JIU_OF

JNG - https://en.wikipedia.org/wiki/Jining_Qufu_Airport
JNG_OA JNG_OF

KMG - https://en.wikipedia.org/wiki/Kunming_Changshui_International_Airport
KMG_OA KMG_OF

KRY - https://en.wikipedia.org/wiki/Karamay_Airport
KRY_OA KRY_OF

LYA - https://en.wikipedia.org/wiki/Luoyang_Beijiao_Airport
LYA_OA LYA_OF

SHE - https://en.wikipedia.org/wiki/Shenyang_Taoxian_International_Airport
SHE_OA SHE_OF

SWA - https://en.wikipedia.org/wiki/Jieyang_Chaoshan_International_Airport
SWA_OA SWA_OF

THQ - https://en.wikipedia.org/wiki/Tianshui_Maijishan_Airport
THQ_OA THQ_OF

TLQ - https://en.wikipedia.org/wiki/Turpan_Jiaohe_Airport
TLQ_OA TLQ_OF

UYN - https://en.wikipedia.org/wiki/Yulin_Yuyang_Airport
UYN_OA UYN_OF

WNH - https://en.wikipedia.org/wiki/Wenshan_Puzhehei_Airport
WNH_OA WNH_OF

XUZ - https://en.wikipedia.org/wiki/Xuzhou_Guanyin_Airport
XUZ_OA XUZ_OF

YNT - https://en.wikipedia.org/wiki/Yantai_Penglai_International_Airport
YNT_OA YNT_OF

As a conclusion for this part, OA seems to be more accurate than OF regarding the airport longitude and latitude. We decide to keep OA data and drop OF data. note we should confirm that openstreetmap satellite is accurate in China and there is not shift phenomenon.

IATA$latitude.deg <- IATA$latitude.deg.x
IATA$longitude.deg <- IATA$longitude.deg.x
IATA <- dplyr::select(IATA, -latitude.deg.x, -latitude.deg.y, -longitude.deg.x, -longitude.deg.y)

Nevertheless as shown in the case of ZLXH airport the data of OA seems inaccurate. Further check is probably needed. note 2 Comparison is done here manually, we will see in another post how to make this detection using machine learning algorithms.

Fixing categories

They are same in both data sets except the following. We decide to keep OF categories.

## mismatch in category
IATA[!(IATA$category.x == IATA$category.y),]$name
## [1] "Altay Air Base"    "Shigatse Air Base"
IATA$category <- IATA$category.y
IATA <- dplyr::select(IATA, -category.x, -category.y)
missmap(IATA)

Fixing elevation.m data

As we have decided to drop OF latitude and longitude we should also drop the elevation from OF data set.

IATA$elevation.m <- IATA$elevation.m.x
IATA <- dplyr::select(IATA, -elevation.m.y, -elevation.m.x)

We have two actions to check and fix elevation data : check if the elevation is consistent with latitude and longitude and find missing elevation data.

We can use for this purpose the jawq API which returns in JSON format the elevation value for a given set of latitude / longitude.

# function which return evelevation based on longitude / latitude
# https://developers.google.com/maps/documentation/elevation/intro

getElevation <- function(lat,long) {
  lat <- as.numeric(lat)
  long <- as.numeric(long)
  # elevation API
  #baseUrl <- "https://api.jawg.io/elevations?locations="
  baseUrl <- "https://maps.googleapis.com/maps/api/elevation/json?locations="

  Url <- paste(baseUrl, lat, "," , long, "&key=", api_elevation, sep = "")
  
  jsonData <- fromJSON(Url)
  #Sys.sleep(1)
  elevation <- jsonData$results$elevation
  elevation
  #Url
}
## get elevation for all aiports
#IATA$elevationCheck <- 1
elevationCheck <- apply(IATA, 1, function(x) getElevation(x[9],x[10]))
IATA$elevationCheck <- elevationCheck

We are now going to check the gap between Google data and OA data set.

## replace missing elevation by google values
IATA[is.na(IATA$elevation.m),]$elevation.m <- IATA[is.na(IATA$elevation.m),]$elevationCheck
dist <- (abs(IATA$elevation.m - IATA$elevationCheck))
hist(dist, breaks = 40)

summary(dist)
##      Min.   1st Qu.    Median      Mean   3rd Qu.      Max. 
##    0.0000    0.1071    2.1109   50.4120    6.2938 2648.6264
dplyr::select(IATA[dist > 30,], IATA.FAA, ICAO, elevation.m, elevationCheck, longitude.deg, latitude.deg)
##     IATA.FAA ICAO elevation.m elevationCheck longitude.deg latitude.deg
## 26       DCY ZUDC    4419.749      4389.1841     100.05333     29.32306
## 46       HEK ZYHE    2605.062       316.7160     127.30888     50.17162
## 79       KCA ZWKC    1076.230      1031.4375      82.87292     41.67786
## 116      NNY ZHNY     256.536       123.4809     112.61500     32.98080
## 122      PZI ZUZH     494.748      1963.4302     101.79852     26.54000
## 123      RKZ ZURK    1155.023      3803.6492      89.31140     29.35190
## 137      TGO ZBTL     731.433       183.6307     122.20000     43.55670
## 178      ZHY ZLZW    2504.891      1237.2886     105.15445     37.57312

We have 8 airports with more than 30 meters of elevation error. After checking, we find out that Jawg data is more accurate and use it for these airports.

IATA[dist > 30,]$elevation.m <- IATA[dist > 30,]$elevationCheck
IATA <- dplyr::select(IATA, -elevationCheck)
missmap(IATA)

We use information from [http://www.geoplaner.com/#Coordinate-Converter] for setting

Saving objects for next part

Let’s now save the different objects to be reused in the next part of this post.

save( list = c("IATA", 
               "airports_2_check"
),
file = "./data/chinese-airports-part-4.Rda")

Code information

Source code

The source code of this post is available on github

Session information

sessionInfo()
## R version 3.6.1 (2017-01-27)
## Platform: x86_64-pc-linux-gnu (64-bit)
## Running under: Ubuntu 16.04.6 LTS
## 
## Matrix products: default
## BLAS:   /home/travis/R-bin/lib/R/lib/libRblas.so
## LAPACK: /home/travis/R-bin/lib/R/lib/libRlapack.so
## 
## locale:
##  [1] LC_CTYPE=en_US.UTF-8          LC_NUMERIC=C                 
##  [3] LC_TIME=en_US.UTF-8           LC_COLLATE=en_US.UTF-8       
##  [5] LC_MONETARY=en_US.UTF-8       LC_MESSAGES=en_US.UTF-8      
##  [7] LC_PAPER=en_US.UTF-8          LC_NAME=en_US.UTF-8          
##  [9] LC_ADDRESS=en_US.UTF-8        LC_TELEPHONE=en_US.UTF-8     
## [11] LC_MEASUREMENT=en_US.UTF-8    LC_IDENTIFICATION=en_US.UTF-8
## 
## attached base packages:
## [1] grid      stats     graphics  grDevices utils     datasets  methods  
## [8] base     
## 
## other attached packages:
##  [1] curl_4.3            purrr_0.3.3         png_0.1-7          
##  [4] raster_3.0-7        glue_1.3.1          slippymath_0.3.1   
##  [7] knitr_1.26          stringdist_0.9.5.5  OpenStreetMap_0.3.4
## [10] Amelia_1.7.6        Rcpp_1.0.3          jsonlite_1.6       
## [13] ggplot2_3.2.1       dplyr_0.8.3         reshape2_1.4.3     
## [16] maptools_0.9-9      sp_1.3-2           
## 
## loaded via a namespace (and not attached):
##  [1] highr_0.8        compiler_3.6.1   pillar_1.4.2     plyr_1.8.5      
##  [5] tools_3.6.1      digest_0.6.23    evaluate_0.14    tibble_2.1.3    
##  [9] lifecycle_0.1.0  gtable_0.3.0     lattice_0.20-38  pkgconfig_2.0.3 
## [13] rlang_0.4.2      parallel_3.6.1   rgdal_1.4-8      yaml_2.2.0      
## [17] blogdown_0.17.1  xfun_0.11        rJava_0.9-11     withr_2.1.2     
## [21] stringr_1.4.0    tidyselect_0.2.5 R6_2.4.1         foreign_0.8-72  
## [25] rmarkdown_1.18   bookdown_0.16    farver_2.0.1     magrittr_1.5    
## [29] codetools_0.2-16 scales_1.1.0     htmltools_0.4.0  assertthat_0.2.1
## [33] colorspace_1.4-1 labeling_0.3     stringi_1.4.3    lazyeval_0.2.2  
## [37] munsell_0.5.0    crayon_1.3.4