Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update 2019b #5

Merged
merged 10 commits into from
Jul 12, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions DESCRIPTION
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Package: lutz
Type: Package
Title: Look Up Time Zones of Point Coordinates
Version: 0.2.0
Version: 0.2.0.999
Authors@R: person("Andy", "Teucher", email = "andy.teucher@gmail.com", role = c("aut", "cre"))
Description: Input latitude and longitude values or an 'sf/sfc' POINT
object and get back the timezone in which they exist. Two methods are implemented.
Expand All @@ -28,5 +28,5 @@ Suggests:
covr
Encoding: UTF-8
LazyData: true
RoxygenNote: 6.0.1
RoxygenNote: 6.1.1
Roxygen: list(markdown = TRUE)
5 changes: 5 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
# lutz 0.2.0.999

* Upgraded timezone map to 2019b
* Dealt with areas with overlapping timezones (#2)

# lutz 0.2.0

* Added `method = "accurate"` to do a slower, but more accurate lookup.
Expand Down
Binary file modified R/sysdata.rda
Binary file not shown.
37 changes: 30 additions & 7 deletions R/tz_lookup.R
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,13 @@
#' incorrect timezone. If accuracy is more important than speed, use
#' `method = "accurate"`.
#'
#' Note that there are some regions in the world where a single point can land in
#' two different overlapping timezones. The `"accurate"` method includes these,
#' and when they are encountered they are concatenated in a single string,
#' separated by a semicolon.
#' The data used in the `"fast"` method does not include overlapping timezones
#' at this time.
#'
#' @param x either an `sfc` or `sf` points or `SpatialPoints(DataFrame)` object
#' @param crs the coordinate reference system: integer with the EPSG code, or character with proj4string.
#' If not specified (i.e., `NULL`) and `x` has no existing `crs`, EPSG: 4326 is assumed (lat/long).
Expand Down Expand Up @@ -36,7 +43,7 @@ tz_lookup <- function(x, crs = NULL, method = "fast", warn = TRUE) {
switch(method,
fast = tz_lookup_fast(x, crs, warn),
accurate = tz_lookup_accurate(x, crs),
stop("method mst be one of 'fast' or 'accurate'", call. = FALSE))
stop("method must be one of 'fast' or 'accurate'", call. = FALSE))

}

Expand Down Expand Up @@ -83,9 +90,9 @@ tz_lookup_coords <- function(lat, lon, method = "fast", warn = TRUE) {
check_coords(lat, lon)

switch(method,
fast = tz_lookup_coords_fast(lat, lon, warn),
accurate = tz_lookup_coords_accurate(lat, lon),
stop("method mst be one of 'fast' or 'accurate'", call. = FALSE)
fast = tz_lookup_coords_fast(lat, lon, warn),
accurate = tz_lookup_coords_accurate(lat, lon),
stop("method must be one of 'fast' or 'accurate'", call. = FALSE)
)
}

Expand All @@ -107,8 +114,24 @@ tz_lookup_accurate <- function(x, crs = NULL) {

tz_lookup_accurate.sf <- function(x, crs = NULL) {
x <- fix_sf(x, crs)
x <- suppressMessages(sf::st_join(x, tz_sf))
ret <- x$tzid
# Add a unique id so we can deal with any duplicates resulting
# from overlapping timezones
x$lutzid <- seq_len(nrow(x))
x_tz <- suppressMessages(sf::st_set_geometry(sf::st_join(x, tz_sf), NULL))

# group x by lutzid and concatenate multiple timezones with ;
if (nrow(x_tz) > nrow(x)) {
warning("Some points are in areas with more than one timezone defined.",
"These are often disputed areas and should be treated with care.")

ret <- stats::aggregate(x_tz, list(x_tz$lutzid), function(x) {
if (length(x) == 1) return(x)
x <- paste(x, collapse = "; ")
}, drop = FALSE)[["tzid"]]

} else {
ret <- x_tz$tzid
}

# If any are NA, try to fill in with V8-based tzlookup
nas <- which(is.na(ret))
Expand All @@ -120,7 +143,7 @@ tz_lookup_accurate.sf <- function(x, crs = NULL) {
}

tz_lookup_accurate.sfc <- function(x, crs = NULL) {
x_sf <- sf::st_sf(id = seq_len(length(x)), geom = x)
x_sf <- sf::st_sf(geom = x)
tz_lookup_accurate(x_sf, crs = crs)
}

Expand Down
24 changes: 20 additions & 4 deletions README.Rmd
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,13 @@ knitr::opts_chunk$set(
)
```

[![Travis-CI Build Status](https://travis-ci.org/ateucher/lutz.svg?branch=master)](https://travis-ci.org/ateucher/lutz) [![AppVeyor Build Status](https://ci.appveyor.com/api/projects/status/github/ateucher/lutz?branch=master&svg=true)](https://ci.appveyor.com/project/ateucher/lutz) [![Coverage Status](https://img.shields.io/codecov/c/github/ateucher/lutz/master.svg)](https://codecov.io/github/ateucher/lutz?branch=master)
<!-- badges: start -->
[![Travis-CI Build Status](https://travis-ci.org/ateucher/lutz.svg?branch=master)](https://travis-ci.org/ateucher/lutz)
[![AppVeyor Build Status](https://ci.appveyor.com/api/projects/status/github/ateucher/lutz?branch=master&svg=true)](https://ci.appveyor.com/project/ateucher/lutz)
[![Coverage Status](https://img.shields.io/codecov/c/github/ateucher/lutz/master.svg)](https://codecov.io/github/ateucher/lutz?branch=master)
[![CRAN status](https://www.r-pkg.org/badges/version/lutz)](https://cran.r-project.org/package=lutz)
[![CRAN downloads](https://cranlogs.r-pkg.org/badges/lutz)](https://cran.r-project.org/package=lutz)
<!-- badges: end -->

# lutz (look up timezones)

Expand Down Expand Up @@ -91,6 +97,10 @@ ggplot(cbind(as.data.frame(coordinates(state_centers_sp)), tz = state_centers_sp
theme_minimal()
```

Note that there are some regions in the world where a single point can land in
two different overlapping timezones. The `"accurate"` method [includes these](https://github.com/evansiroky/timezone-boundary-builder/releases/tag/2018g),
however the method used in the `"fast"` does not include overlapping timezones
([at least for now](https://github.com/darkskyapp/tz-lookup/issues/34)).

We can compare the accuracy of both methods to the high-resolution timezone map
provided by https://github.com/evansiroky/timezone-boundary-builder. This is the
Expand All @@ -99,7 +109,7 @@ simplified by about 80% to be small enough to fit in the package.

```{r eval=FALSE}
## Get the full timezone geojson from https://github.com/evansiroky/timezone-boundary-builder
download.file("https://github.com/evansiroky/timezone-boundary-builder/releases/download/2018d/timezones-with-oceans.geojson.zip",
download.file("https://github.com/evansiroky/timezone-boundary-builder/releases/download/2019a/timezones-with-oceans.geojson.zip",
destfile = "tz.zip")
unzip("tz.zip", exdir = "data-raw/dist/")
```
Expand All @@ -108,19 +118,25 @@ unzip("tz.zip", exdir = "data-raw/dist/")
```{r eval=file.exists("data-raw/dist/combined-with-oceans.json")}
library(lutz)
library(sf)
library(rmapshaper)
library(purrr)
library(dplyr)

tz_full <- read_sf("data-raw/dist/combined-with-oceans.json")
# Create a data frame of 500000 lat/long pairs:
set.seed(1)
n <- 500000
ll <- data.frame(lat = runif(n, -90, 90), lon = runif(n, -180, 180))
ll <- data.frame(id = seq(n), lat = runif(n, -90, 90), lon = runif(n, -180, 180))
ll_sf <- st_as_sf(ll, coords = c("lon", "lat"), crs = 4326)

# Overlay those points with the full high-resolution timezone map:
ref_ll_tz <- sf::st_join(ll_sf, tz_full)

# Combine those that had overlapping timezones
ref_ll_tz <- ref_ll_tz %>%
st_set_geometry(NULL) %>%
group_by(id) %>%
summarize(tzid = paste(tzid, collapse = "; "))

# run tz_lookup with both `"fast"` and `"accurate"` methods and compare with
# the timezones looked up with the high-resolution map:
tests <- map_df(c("fast", "accurate"), ~ {
Expand Down
46 changes: 39 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@

<!-- README.md is generated from README.Rmd. Please edit that file -->

<!-- badges: start -->

[![Travis-CI Build
Status](https://travis-ci.org/ateucher/lutz.svg?branch=master)](https://travis-ci.org/ateucher/lutz)
[![AppVeyor Build
Status](https://ci.appveyor.com/api/projects/status/github/ateucher/lutz?branch=master&svg=true)](https://ci.appveyor.com/project/ateucher/lutz)
[![Coverage
Status](https://img.shields.io/codecov/c/github/ateucher/lutz/master.svg)](https://codecov.io/github/ateucher/lutz?branch=master)
[![CRAN
status](https://www.r-pkg.org/badges/version/lutz)](https://cran.r-project.org/package=lutz)
[![CRAN
downloads](https://cranlogs.r-pkg.org/badges/lutz)](https://cran.r-project.org/package=lutz)
<!-- badges: end -->

# lutz (look up timezones)

Expand Down Expand Up @@ -99,36 +106,58 @@ ggplot(cbind(as.data.frame(coordinates(state_centers_sp)), tz = state_centers_sp

![](tools/readme/unnamed-chunk-5-1.png)<!-- -->

Note that there are some regions in the world where a single point can
land in two different overlapping timezones. The `"accurate"` method
[includes
these](https://github.com/evansiroky/timezone-boundary-builder/releases/tag/2018g),
however the method used in the `"fast"` does not include overlapping
timezones ([at least for
now](https://github.com/darkskyapp/tz-lookup/issues/34)).

We can compare the accuracy of both methods to the high-resolution
timezone map provided by
<https://github.com/evansiroky/timezone-boundary-builder>. This is the
map that is used by `lutz` for the `"accurate"` method, but in `lutz` it
is simplified by about 80% to be small enough to fit in the
package.
is simplified by about 80% to be small enough to fit in the package.

``` r
## Get the full timezone geojson from https://github.com/evansiroky/timezone-boundary-builder
download.file("https://github.com/evansiroky/timezone-boundary-builder/releases/download/2018d/timezones-with-oceans.geojson.zip",
download.file("https://github.com/evansiroky/timezone-boundary-builder/releases/download/2019a/timezones-with-oceans.geojson.zip",
destfile = "tz.zip")
unzip("tz.zip", exdir = "data-raw/dist/")
```

``` r
library(lutz)
library(sf)
library(rmapshaper)
library(purrr)
library(dplyr)
#>
#> Attaching package: 'dplyr'
#> The following objects are masked from 'package:stats':
#>
#> filter, lag
#> The following objects are masked from 'package:base':
#>
#> intersect, setdiff, setequal, union

tz_full <- read_sf("data-raw/dist/combined-with-oceans.json")
# Create a data frame of 500000 lat/long pairs:
set.seed(1)
n <- 500000
ll <- data.frame(lat = runif(n, -90, 90), lon = runif(n, -180, 180))
ll <- data.frame(id = seq(n), lat = runif(n, -90, 90), lon = runif(n, -180, 180))
ll_sf <- st_as_sf(ll, coords = c("lon", "lat"), crs = 4326)

# Overlay those points with the full high-resolution timezone map:
ref_ll_tz <- sf::st_join(ll_sf, tz_full)
#> although coordinates are longitude/latitude, st_intersects assumes that they are planar
#> although coordinates are longitude/latitude, st_intersects assumes that they are planar

# Combine those that had overlapping timezones
ref_ll_tz <- ref_ll_tz %>%
st_set_geometry(NULL) %>%
group_by(id) %>%
summarize(tzid = paste(tzid, collapse = "; "))

# run tz_lookup with both `"fast"` and `"accurate"` methods and compare with
# the timezones looked up with the high-resolution map:
Expand All @@ -147,6 +176,9 @@ tests <- map_df(c("fast", "accurate"), ~ {
fun_nas = sum(is.na(test_ll_tz))
)
})
#> Warning in tz_lookup_accurate.sf(x, crs): Some points are in areas with
#> more than one timezone defined.These are often disputed areas and should be
#> treated with care.
```

``` r
Expand All @@ -155,5 +187,5 @@ knitr::kable(tests)

| method | time | matches | mismatches | accuracy | ref\_nas | fun\_nas |
| :------- | -----: | ------: | ---------: | -------: | -------: | -------: |
| fast | 2.721 | 384735 | 115265 | 0.769470 | 0 | 0 |
| accurate | 47.587 | 499956 | 44 | 0.999912 | 0 | 0 |
| fast | 2.453 | 371953 | 128047 | 0.743906 | 0 | 0 |
| accurate | 26.328 | 499949 | 51 | 0.999898 | 0 | 0 |
8 changes: 5 additions & 3 deletions data-raw/tz_sf.R
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
library(sf)
library(lwgeom)
library(rmapshaper)
# The data is from https://github.com/evansiroky/timezone-boundary-builder
# And released under the Open Data Commons Open Database License (ODbL)
download.file("https://github.com/evansiroky/timezone-boundary-builder/releases/download/2018d/timezones-with-oceans.geojson.zip",
download.file("https://github.com/evansiroky/timezone-boundary-builder/releases/download/2019b/timezones-with-oceans.geojson.zip",
destfile = "data-raw/tz.zip")
unzip("data-raw/tz.zip", exdir = "data-raw", overwrite = TRUE)
tz_full <- read_sf(list.files("data-raw", recursive = TRUE, pattern = "oceans\\.json$", full.names = TRUE))
tz_sf <- ms_simplify(tz_full, keep = 0.17, keep_shapes = TRUE, explode = TRUE)
tz_sf <- ms_simplify(tz_full, keep = 0.135, keep_shapes = TRUE, explode = TRUE) %>%
st_make_valid()

devtools::use_data(tz_sf, compress = "xz", internal = TRUE, overwrite = TRUE)
usethis::use_data(tz_sf, compress = "xz", internal = TRUE, overwrite = TRUE)
4 changes: 2 additions & 2 deletions inst/tz-lookup/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

tz-lookup is licensed CC0.

Find the latest release (v6.1.8) at https://github.com/darkskyapp/tz-lookup/releases
Find the latest release (v6.1.21) at https://github.com/darkskyapp/tz-lookup/releases

```
cd inst/tz-lookup
wget -q https://raw.githubusercontent.com/darkskyapp/tz-lookup/v6.1.8/tz.js -O tz.js
wget -q https://raw.githubusercontent.com/darkskyapp/tz-lookup/v6.1.21/tz.js -O tz.js
```
2 changes: 1 addition & 1 deletion inst/tz-lookup/tz.js

Large diffs are not rendered by default.

8 changes: 8 additions & 0 deletions man/tz_lookup.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions tests/testthat/test-tz_lookup.R
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ test_that("warn works with tz_lookup", {

test_that("errors when method is not one of fast, accurate", {
expect_error(tz_lookup_coords(70,30, method = "great"),
"method mst be one of 'fast' or 'accurate'")
"method must be one of 'fast' or 'accurate'")
expect_error(tz_lookup(sf::st_sfc(sf::st_point(c(1,1))), method = "great"),
"method mst be one of 'fast' or 'accurate'")
"method must be one of 'fast' or 'accurate'")
})

test_that("tz_lookup_coords works", {
Expand Down
15 changes: 15 additions & 0 deletions tests/testthat/test-tz_lookup2.R
Original file line number Diff line number Diff line change
Expand Up @@ -51,3 +51,18 @@ test_that("tz_lookup.SpatialPoints works", {
"Etc/GMT+9")
expect_equal(tz_lookup(pt, 3005, method = "accurate"), "Etc/GMT+9")
})

test_that("tz_lookup accurate method deals with overlapping timezones", {
coords <- c(85.18436, 41.11543) # centroid of https://en.wikipedia.org/wiki/Xinjiang_Time
expect_warning(tz <- tz_lookup_coords(coords[2], coords[1], method = "accurate"),
"Some points are in areas with more than one timezone")
expect_equal(tz, "Asia/Shanghai; Asia/Urumqi")
coords_sfc <- sf::st_sfc(sf::st_point(coords), crs = 4326)
expect_warning(tz <- tz_lookup(coords_sfc, method = "accurate"),
"Some points are in areas with more than one timezone")
expect_equal(tz, "Asia/Shanghai; Asia/Urumqi")
coords_sp <- as(coords_sfc, "Spatial")
expect_warning(tz <- tz_lookup(coords_sp, method = "accurate"),
"Some points are in areas with more than one timezone")
expect_equal(tz, "Asia/Shanghai; Asia/Urumqi")
})
Binary file modified tools/readme/unnamed-chunk-4-1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified tools/readme/unnamed-chunk-5-1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.