├── .github
├── .gitignore
└── workflows
│ ├── pkgdown.yaml
│ └── R-CMD-check.yaml
├── vignettes
├── .gitignore
├── README-unnamed-chunk-10-1.png
├── README-unnamed-chunk-11-1.png
├── README-unnamed-chunk-12-1.png
├── README-unnamed-chunk-13-1.png
├── README-unnamed-chunk-2-1.png
├── README-unnamed-chunk-3-1.png
├── README-unnamed-chunk-4-1.png
├── README-unnamed-chunk-5-1.png
├── README-unnamed-chunk-6-1.png
├── README-unnamed-chunk-7-1.png
├── README-unnamed-chunk-8-1.png
├── README-unnamed-chunk-9-1.png
└── graticule.Rmd
├── .gitignore
├── tests
├── testthat.R
└── testthat
│ ├── test-tiles.R
│ ├── test-no-longitude-warnings.R
│ └── testgraticule.R
├── man
├── figures
│ ├── README-lonlat-1.png
│ ├── README-lonlat-2.png
│ ├── README-lonlat-3.png
│ ├── README-wedge-1.png
│ ├── README-example-1.png
│ └── README-rhumb-points-1.png
├── graticule_labels.Rd
├── graticule.Rd
└── lonlat.Rd
├── inst
└── extdata
│ ├── nt_20140320_f17_v01_s.bin
│ └── getice.R
├── R
├── utils.R
├── zzz.R
├── pathos.R
├── lonlat.R
├── graticule2.R
└── graticule.R
├── cran-comments.md
├── .Rbuildignore
├── graticule.Rproj
├── NAMESPACE
├── DESCRIPTION
├── CODE_OF_CONDUCT.md
├── NEWS.md
├── README.Rmd
└── README.md
/.github/.gitignore:
--------------------------------------------------------------------------------
1 | *.html
2 |
--------------------------------------------------------------------------------
/vignettes/.gitignore:
--------------------------------------------------------------------------------
1 | *.html
2 | *.R
3 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .Rproj.user
2 | .Rhistory
3 | .RData
4 | inst/doc
5 |
--------------------------------------------------------------------------------
/tests/testthat.R:
--------------------------------------------------------------------------------
1 | library(testthat)
2 | test_check("graticule")
3 |
--------------------------------------------------------------------------------
/man/figures/README-lonlat-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hypertidy/graticule/HEAD/man/figures/README-lonlat-1.png
--------------------------------------------------------------------------------
/man/figures/README-lonlat-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hypertidy/graticule/HEAD/man/figures/README-lonlat-2.png
--------------------------------------------------------------------------------
/man/figures/README-lonlat-3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hypertidy/graticule/HEAD/man/figures/README-lonlat-3.png
--------------------------------------------------------------------------------
/man/figures/README-wedge-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hypertidy/graticule/HEAD/man/figures/README-wedge-1.png
--------------------------------------------------------------------------------
/man/figures/README-example-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hypertidy/graticule/HEAD/man/figures/README-example-1.png
--------------------------------------------------------------------------------
/inst/extdata/nt_20140320_f17_v01_s.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hypertidy/graticule/HEAD/inst/extdata/nt_20140320_f17_v01_s.bin
--------------------------------------------------------------------------------
/man/figures/README-rhumb-points-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hypertidy/graticule/HEAD/man/figures/README-rhumb-points-1.png
--------------------------------------------------------------------------------
/vignettes/README-unnamed-chunk-10-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hypertidy/graticule/HEAD/vignettes/README-unnamed-chunk-10-1.png
--------------------------------------------------------------------------------
/vignettes/README-unnamed-chunk-11-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hypertidy/graticule/HEAD/vignettes/README-unnamed-chunk-11-1.png
--------------------------------------------------------------------------------
/vignettes/README-unnamed-chunk-12-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hypertidy/graticule/HEAD/vignettes/README-unnamed-chunk-12-1.png
--------------------------------------------------------------------------------
/vignettes/README-unnamed-chunk-13-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hypertidy/graticule/HEAD/vignettes/README-unnamed-chunk-13-1.png
--------------------------------------------------------------------------------
/vignettes/README-unnamed-chunk-2-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hypertidy/graticule/HEAD/vignettes/README-unnamed-chunk-2-1.png
--------------------------------------------------------------------------------
/vignettes/README-unnamed-chunk-3-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hypertidy/graticule/HEAD/vignettes/README-unnamed-chunk-3-1.png
--------------------------------------------------------------------------------
/vignettes/README-unnamed-chunk-4-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hypertidy/graticule/HEAD/vignettes/README-unnamed-chunk-4-1.png
--------------------------------------------------------------------------------
/vignettes/README-unnamed-chunk-5-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hypertidy/graticule/HEAD/vignettes/README-unnamed-chunk-5-1.png
--------------------------------------------------------------------------------
/vignettes/README-unnamed-chunk-6-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hypertidy/graticule/HEAD/vignettes/README-unnamed-chunk-6-1.png
--------------------------------------------------------------------------------
/vignettes/README-unnamed-chunk-7-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hypertidy/graticule/HEAD/vignettes/README-unnamed-chunk-7-1.png
--------------------------------------------------------------------------------
/vignettes/README-unnamed-chunk-8-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hypertidy/graticule/HEAD/vignettes/README-unnamed-chunk-8-1.png
--------------------------------------------------------------------------------
/vignettes/README-unnamed-chunk-9-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hypertidy/graticule/HEAD/vignettes/README-unnamed-chunk-9-1.png
--------------------------------------------------------------------------------
/R/utils.R:
--------------------------------------------------------------------------------
1 | #' @importFrom raster xmin xmax
2 | xrange <- function(x) {
3 | c(xmin(x), xmax(x))
4 | }
5 | #' @importFrom raster ymin ymax
6 | yrange <- function(x) {
7 | c(ymin(x), ymax(x))
8 | }
9 |
--------------------------------------------------------------------------------
/R/zzz.R:
--------------------------------------------------------------------------------
1 |
2 | .onLoad <- function(libname, pkgname) {
3 | op <- options()
4 | op.graticule <- list(
5 | graticule.mindist = 5e4
6 | )
7 | toset <- !(names(op.graticule) %in% names(op))
8 | if (any(toset)) options(op.graticule[toset])
9 |
10 | invisible()
11 | }
12 |
--------------------------------------------------------------------------------
/inst/extdata/getice.R:
--------------------------------------------------------------------------------
1 | icefile <- "ftp://sidads.colorado.edu/pub/DATASETS/nsidc0051_gsfc_nasateam_seaice/final-gsfc/south/daily/2014/nt_20140320_f17_v01_s.bin"
2 | tfile <- file.path("inst", "extdata", basename(icefile))
3 | if (!file.exists(tfile)) download.file(icefile, tfile, mode = "wb")
4 |
--------------------------------------------------------------------------------
/cran-comments.md:
--------------------------------------------------------------------------------
1 | ## graticule 0.3.0
2 |
3 | Fixed failing tests.
4 |
5 | Thank you!
6 |
7 | ## Test environments
8 |
9 | * win-builder (release and devel)
10 | * Linux local release, github actions
11 | * macbuilder
12 |
13 | ## R CMD check results
14 |
15 | There were no ERRORs or WARNINGs or NOTEs.
16 |
17 |
--------------------------------------------------------------------------------
/.Rbuildignore:
--------------------------------------------------------------------------------
1 | ^.*\.Rproj$
2 | ^\.Rproj\.user$
3 | ./*.png
4 | ./README.Rmd
5 | .gitignore
6 | cran-comments.md
7 | vignettes/*.png
8 | ^\.travis\.yml$
9 | ^README\.Rmd$
10 | ^README-.*\.png$
11 | docs/*
12 | revdep/*
13 | ^CODE_OF_CONDUCT\.md$
14 | ^appveyor\.yml$
15 | ^codecov\.yml$
16 | ^CRAN-RELEASE$
17 | ^\.github$
18 |
--------------------------------------------------------------------------------
/tests/testthat/test-tiles.R:
--------------------------------------------------------------------------------
1 | test_that("tiles is sensible ", {
2 | g <- graticule::graticule(seq(-180, 180, by = 15), lat = c(-45, -43), tiles = TRUE)
3 | expect_true(nrow(g) == 24)
4 | expect_equivalent(extent(g), extent(-180, 180, -45, -43))
5 |
6 | expect_error(graticule(89, tiles = TRUE))
7 | expect_error(graticule(c(10, 11), 5, tiles = TRUE))
8 | })
9 |
--------------------------------------------------------------------------------
/tests/testthat/test-no-longitude-warnings.R:
--------------------------------------------------------------------------------
1 | lon <- seq(170, 350, by = 10)
2 | lat <- c(-50, -40)
3 |
4 | test_that("no longitude warnings", {
5 | expect_s4_class(graticule(lon, lat), "SpatialLinesDataFrame")
6 |
7 | expect_s4_class(graticule(lon, lat, tiles = TRUE), "SpatialPolygonsDataFrame")
8 |
9 | expect_s4_class(graticule_labels(lon, lat), "SpatialPointsDataFrame")
10 | })
11 |
--------------------------------------------------------------------------------
/graticule.Rproj:
--------------------------------------------------------------------------------
1 | Version: 1.0
2 |
3 | RestoreWorkspace: No
4 | SaveWorkspace: No
5 | AlwaysSaveHistory: Default
6 |
7 | EnableCodeIndexing: Yes
8 | UseSpacesForTab: Yes
9 | NumSpacesForTab: 2
10 | Encoding: UTF-8
11 |
12 | RnwWeave: Sweave
13 | LaTeX: pdfLaTeX
14 |
15 | AutoAppendNewline: Yes
16 | StripTrailingWhitespace: Yes
17 |
18 | BuildType: Package
19 | PackageUseDevtools: Yes
20 | PackageInstallArgs: --no-multiarch --with-keep.source
21 | PackageRoxygenize: rd,collate,namespace,vignette
22 |
--------------------------------------------------------------------------------
/tests/testthat/testgraticule.R:
--------------------------------------------------------------------------------
1 | context("Basic function")
2 |
3 | test_that("graticule creation is successful", {
4 | expect_that(graticule(seq(100, 240, by = 15), seq(-85, -30, by = 15)), is_a("SpatialLinesDataFrame"))
5 | expect_that(graticule(seq(100, 240, by = 15), seq(-85, -30, by = 15), nverts = 20), is_a("SpatialLinesDataFrame"))
6 | expect_that(graticule(seq(100, 240, by = 15), seq(-85, -30, by = 15), nverts = 100, xlim = c(-180, 180), ylim = c(-60, -30)), is_a("SpatialLinesDataFrame"))
7 |
8 | })
9 |
10 | test_that("labels work", {
11 | expect_that(graticule_labels(seq(100, 240, by = 15), seq(-85, -30, by = 15)), is_a("SpatialPointsDataFrame"))
12 | })
13 |
14 |
--------------------------------------------------------------------------------
/NAMESPACE:
--------------------------------------------------------------------------------
1 | # Generated by roxygen2: do not edit by hand
2 |
3 | export(graticule)
4 | export(graticule_labels)
5 | export(lonlat)
6 | importFrom(graphics,contour)
7 | importFrom(raster,"values<-")
8 | importFrom(raster,extent)
9 | importFrom(raster,isLonLat)
10 | importFrom(raster,ncell)
11 | importFrom(raster,raster)
12 | importFrom(raster,res)
13 | importFrom(raster,xmax)
14 | importFrom(raster,xmin)
15 | importFrom(raster,ymax)
16 | importFrom(raster,ymin)
17 | importFrom(reproj,reproj_xy)
18 | importFrom(sp,"coordinates<-")
19 | importFrom(sp,"proj4string<-")
20 | importFrom(sp,CRS)
21 | importFrom(sp,Line)
22 | importFrom(sp,Lines)
23 | importFrom(sp,SpatialLines)
24 | importFrom(sp,SpatialLinesDataFrame)
25 | importFrom(sp,degreeLabelsEW)
26 | importFrom(sp,degreeLabelsNS)
27 | importFrom(stats,approx)
28 | importFrom(utils,head)
29 |
--------------------------------------------------------------------------------
/DESCRIPTION:
--------------------------------------------------------------------------------
1 | Package: graticule
2 | Type: Package
3 | Title: Meridional and Parallel Lines for Maps
4 | Version: 0.3.0
5 | Authors@R: person("Michael D.","Sumner", role = c("aut", "cre"), email =
6 | "mdsumner@gmail.com")
7 | Description: Create graticule lines and labels for maps. Control the creation
8 | of lines or tiles by setting their placement (at particular meridians and parallels)
9 | and extent (along parallels and meridians). Labels are created independently of
10 | lines.
11 | License: GPL-3
12 | Depends:
13 | sp
14 | Imports:
15 | raster,
16 | utils,
17 | geosphere,
18 | stats,
19 | reproj (>= 0.4.3)
20 | Suggests:
21 | sessioninfo,
22 | knitr,
23 | spex,
24 | testthat (>= 2.1.0),
25 | rmarkdown,
26 | covr
27 | VignetteBuilder: knitr
28 | Encoding: UTF-8
29 | BugReports: https://github.com/hypertidy/graticule/issues
30 | URL: https://github.com/hypertidy/graticule, https://hypertidy.github.io/graticule/
31 | RoxygenNote: 7.2.3
32 |
--------------------------------------------------------------------------------
/man/graticule_labels.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/graticule.R
3 | \name{graticule_labels}
4 | \alias{graticule_labels}
5 | \title{Create graticule labels.}
6 | \usage{
7 | graticule_labels(lons, lats, xline, yline, proj = NULL)
8 | }
9 | \arguments{
10 | \item{lons}{longitudes for meridional labels}
11 |
12 | \item{lats}{latitudes for parallel labels}
13 |
14 | \item{xline}{meridian/s for placement of parallel labels}
15 |
16 | \item{yline}{parallel/s for placement of meridian labels}
17 |
18 | \item{proj}{optional proj.4 string for output object}
19 | }
20 | \value{
21 | SpatialPoints object with labels for downstream use
22 | }
23 | \description{
24 | Returns a set of points with labels, for plotting in conjuction with \code{\link{graticule}}.
25 | }
26 | \details{
27 | SpatialPoints are returned in the projection of \code{proj} if given, or longlat / WGS84.
28 | }
29 | \examples{
30 | xx <- c(100, 120, 160, 180)
31 | yy <- c(-80,-70,-60, -50,-45, -30)
32 | prj <- "+proj=lcc +lon_0=150 +lat_0=-80 +lat_1=-85 +lat_2=-75 +ellps=WGS84"
33 | plot(graticule(lons = xx, lats = yy, proj = prj))
34 | labs <- graticule_labels(lons = xx, lats = yy, xline = 100, yline = -80, proj = prj)
35 | op <- par(xpd = NA)
36 | text(labs, lab = parse(text = labs$lab), pos = c(2, 1)[labs$islon + 1], adj = 1.2)
37 | par(op)
38 | }
39 |
--------------------------------------------------------------------------------
/.github/workflows/pkgdown.yaml:
--------------------------------------------------------------------------------
1 | # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples
2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help
3 | on:
4 | push:
5 | branches: [main, master]
6 | pull_request:
7 | branches: [main, master]
8 | release:
9 | types: [published]
10 | workflow_dispatch:
11 |
12 | name: pkgdown
13 |
14 | jobs:
15 | pkgdown:
16 | runs-on: ubuntu-latest
17 | # Only restrict concurrency for non-PR jobs
18 | concurrency:
19 | group: pkgdown-${{ github.event_name != 'pull_request' || github.run_id }}
20 | env:
21 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}
22 | steps:
23 | - uses: actions/checkout@v3
24 |
25 | - uses: r-lib/actions/setup-pandoc@v2
26 |
27 | - uses: r-lib/actions/setup-r@v2
28 | with:
29 | use-public-rspm: true
30 |
31 | - uses: r-lib/actions/setup-r-dependencies@v2
32 | with:
33 | extra-packages: any::pkgdown, local::.
34 | needs: website
35 |
36 | - name: Build site
37 | run: pkgdown::build_site_github_pages(new_process = FALSE, install = FALSE)
38 | shell: Rscript {0}
39 |
40 | - name: Deploy to GitHub pages 🚀
41 | if: github.event_name != 'pull_request'
42 | uses: JamesIves/github-pages-deploy-action@v4.4.1
43 | with:
44 | clean: false
45 | branch: gh-pages
46 | folder: docs
47 |
--------------------------------------------------------------------------------
/R/pathos.R:
--------------------------------------------------------------------------------
1 |
2 | # Create a mesh of evenly spaced lines in another projection.
3 | #
4 | # @param x object to build line mesh for
5 | # @param proj the other projection
6 | #
7 | # @return spatial object
8 | # @export
9 | #
10 | # @examples
11 | # \dontrun{
12 | # library(maptools)
13 | # data(wrld_simpl)
14 | # library(raster)
15 | # w <- subset(wrld_simpl, NAME == "Australia")
16 | # plot(w)
17 | # laea <- pathologicule(w, "+proj=laea +lon_0=147 +lat_0=-42 +ellps=WGS84")
18 | # stere <- pathologicule(w, "+proj=stere +lon_0=147 +lat_0=-42 +ellps=WGS84")
19 | # plot(laea, add = TRUE, col = "dodgerblue")
20 | # plot(stere, add = TRUE, col = "firebrick")
21 | #
22 | # stere <- "+proj=stere +lat_0=-90 +ellps=WGS84"
23 | # p <- spTransform(subset(wrld_simpl, coordinates(wrld_simpl)[,2] < -20), stere)
24 | # plot(extent(p) + 1e6, asp = 1, type = "n"); plot(p, add = TRUE)
25 | # laea <- pathologicule(p, "+proj=laea +lon_0=147 +lat_0=-72 +ellps=WGS84")
26 | # stere <- pathologicule(p, "+proj=stere +lon_0=147 +lat_0=-42 +ellps=WGS84")
27 | # plot(laea, add = TRUE, col = "dodgerblue")
28 | # plot(stere, add = TRUE, col = "firebrick")
29 | # }
30 | # gridicule <- function(x, proj) {
31 | # y <- spTransform(x, proj)
32 | # xr <- xrange(y)
33 | # yr <- yrange(y)
34 | # xs <- seq(xr[1], xr[2], length = 15)
35 | # ys <- seq(yr[1], yr[2], length = 15)
36 | # g <- graticule(xs, ys)
37 | # projection(g) <- proj
38 | # spTransform(g, projection(x))
39 | # }
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Code of Conduct
2 |
3 | As contributors and maintainers of this project, we pledge to respect all people who
4 | contribute through reporting issues, posting feature requests, updating documentation,
5 | submitting pull requests or patches, and other activities.
6 |
7 | We are committed to making participation in this project a harassment-free experience for
8 | everyone, regardless of level of experience, gender, gender identity and expression,
9 | sexual orientation, disability, personal appearance, body size, race, ethnicity, age, or religion.
10 |
11 | Examples of unacceptable behavior by participants include the use of sexual language or
12 | imagery, derogatory comments or personal attacks, trolling, public or private harassment,
13 | insults, or other unprofessional conduct.
14 |
15 | Project maintainers have the right and responsibility to remove, edit, or reject comments,
16 | commits, code, wiki edits, issues, and other contributions that are not aligned to this
17 | Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed
18 | from the project team.
19 |
20 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by
21 | opening an issue or contacting one or more of the project maintainers.
22 |
23 | This Code of Conduct is adapted from the Contributor Covenant
24 | (https://www.contributor-covenant.org), version 1.0.0, available at
25 | https://contributor-covenant.org/version/1/0/0/.
26 |
--------------------------------------------------------------------------------
/.github/workflows/R-CMD-check.yaml:
--------------------------------------------------------------------------------
1 | # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples
2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help
3 | on:
4 | push:
5 | branches: [main, master]
6 | pull_request:
7 | branches: [main, master]
8 |
9 | name: R-CMD-check
10 |
11 | jobs:
12 | R-CMD-check:
13 | runs-on: ${{ matrix.config.os }}
14 |
15 | name: ${{ matrix.config.os }} (${{ matrix.config.r }})
16 |
17 | strategy:
18 | fail-fast: false
19 | matrix:
20 | config:
21 | - {os: macos-latest, r: 'release'}
22 | - {os: windows-latest, r: 'release'}
23 | - {os: ubuntu-latest, r: 'devel', http-user-agent: 'release'}
24 | - {os: ubuntu-latest, r: 'release'}
25 | - {os: ubuntu-latest, r: 'oldrel-1'}
26 |
27 | env:
28 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}
29 | R_KEEP_PKG_SOURCE: yes
30 |
31 | steps:
32 | - uses: actions/checkout@v3
33 |
34 | - uses: r-lib/actions/setup-pandoc@v2
35 |
36 | - uses: r-lib/actions/setup-r@v2
37 | with:
38 | r-version: ${{ matrix.config.r }}
39 | http-user-agent: ${{ matrix.config.http-user-agent }}
40 | use-public-rspm: true
41 |
42 | - uses: r-lib/actions/setup-r-dependencies@v2
43 | with:
44 | extra-packages: any::rcmdcheck
45 | needs: check
46 |
47 | - uses: r-lib/actions/check-r-package@v2
48 | with:
49 | upload-snapshots: true
50 |
--------------------------------------------------------------------------------
/man/graticule.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/graticule.R
3 | \docType{package}
4 | \name{graticule}
5 | \alias{graticule}
6 | \title{graticule: graticule lines for maps}
7 | \usage{
8 | graticule(lons, lats, nverts = NULL, xlim, ylim, proj = NULL, tiles = FALSE)
9 | }
10 | \arguments{
11 | \item{lons}{longitudes for meridional lines}
12 |
13 | \item{lats}{latitudes for parallel lines}
14 |
15 | \item{nverts}{number of discrete vertices for each segment}
16 |
17 | \item{xlim}{maximum range of parallel lines}
18 |
19 | \item{ylim}{maximum range of meridional lines}
20 |
21 | \item{proj}{optional proj.4 string for output object}
22 |
23 | \item{tiles}{if \code{TRUE} return polygons as output}
24 | }
25 | \value{
26 | SpatialLines or SpatialPolygons object
27 | }
28 | \description{
29 | Specify the creation of lines along meridians by specifying their placement
30 | at particular \code{lons} (longitudes) and \code{lats} (latitudes) and their extents
31 | with \code{xlim} (extent of parallel line in longitude) and \code{ylim} (extent of meridional line in latitude).
32 | }
33 | \details{
34 | Provide a valid PROJ.4 string to return the graticule lines in this projection. If this is not specified the graticule
35 | lines are returned in their original longlat / WGS84.
36 | All segments are discretized as _rhumb_lines_ at `getOption("graticule.mindist")` metres, which
37 | defaults to `5e4`.
38 | The arguments \code{xlim}, \code{ylim} and \code{nverts} are ignored if \code{tiles} is \code{TRUE}.
39 | }
40 | \examples{
41 | graticule()
42 | }
43 |
--------------------------------------------------------------------------------
/man/lonlat.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/lonlat.R
3 | \name{lonlat}
4 | \alias{lonlat}
5 | \title{Add longitude latitude lines to a plot}
6 | \usage{
7 | lonlat(
8 | x,
9 | na.rm = FALSE,
10 | lon = FALSE,
11 | lat = FALSE,
12 | ...,
13 | plot = TRUE,
14 | add = TRUE
15 | )
16 | }
17 | \arguments{
18 | \item{x}{input raster}
19 |
20 | \item{na.rm}{logical, remove missing values from generated coordinates}
21 |
22 | \item{lon}{if TRUE, only longitude plotted}
23 |
24 | \item{lat}{if TRUE (and `lon = FALSE`) only latitude plotted}
25 |
26 | \item{...}{passed to [graphics::contour()]}
27 |
28 | \item{plot}{logical, plot the result}
29 |
30 | \item{add}{logical, add to current plot or instantiate one}
31 | }
32 | \value{
33 | RasterBrick of the longitude and latitude values, two layers
34 |
35 | (invisibly) the raster (RasterBrick) object with longitude and latitude values of the input
36 | as two layers, otherwise this function used for side-effect (drawing on a plot)
37 | }
38 | \description{
39 | Use the coordinates of the input raster to generate coordinate rasters, these are
40 | then used in a contour plot.
41 | }
42 | \details{
43 | Plot is added to an existing plot by default.
44 | }
45 | \examples{
46 | plot(c(-180, 180), c(-90, 90))
47 | lonlat(raster::raster())
48 |
49 | p <- raster::projectExtent(raster::raster(), "+proj=igh")
50 | lonlat(p, add = FALSE)
51 | lonlat(p, levels = seq(-180, 180, by =15), add = FALSE)
52 |
53 | lonlat(p, levels = seq(-180, 180, by = 5), add = FALSE, lon = TRUE)
54 | lonlat(p, levels = seq(-180, 180, by = 15), add = TRUE, lat = TRUE)
55 | }
56 |
--------------------------------------------------------------------------------
/NEWS.md:
--------------------------------------------------------------------------------
1 | # graticule 0.3.0
2 |
3 | * Remove checks for tests upset by package startup messages.
4 |
5 | # graticule 0.2.0
6 |
7 | * Now return values of functions are documented.
8 |
9 | * Now import the reproj package for the coordinate transformation support.
10 |
11 | * Removed function `pathologicule()`, no one will miss it. Might reappear as {gridicule} when we have
12 | better PROJ support.
13 |
14 | * Removed unused methods package from Imports.
15 |
16 | * Removed rgdal, maptools, rworldmap, oce from Suggests, and tested check succeeds without
17 | those being installed.
18 |
19 | * New function `lonlat()` for quick and dirty plots and to generate fields of longitude and
20 | latitude.
21 |
22 | * update for #16, not great still but better
23 |
24 |
25 |
26 | # graticule 0.1.6
27 |
28 | * Release to fix problems on CRAN, unused LazyData and dep on rmarkdown.
29 |
30 | * Remove warnings from geosphere about longitude, thanks to @Maschette for the suggestion.
31 |
32 | * Fix bug caused by new tile creation. https://github.com/AustralianAntarcticDivision/SOmap/issues/66
33 |
34 | * Tile and line graticules are now created by the same process, which discretizes
35 | to a default value of 5e4m (50km). This is settable with `options(graticule.mindist = )`.
36 |
37 | * graticule no longer shares the extra dependency from raster on rgeos, rasterToPolygons is no longer used
38 |
39 | * Added supporting information to the package.
40 |
41 |
42 | VERSION 0.1.2
43 |
44 | o specify LCC standard parallels explicitly to avoid problems from some PROJ.4 installations
45 |
46 | VERSION 0.1.0
47 |
48 | o new function pathologicule to draw the other projection
49 |
50 | VERSION 0.0.3
51 |
52 | o upgraded for release
53 |
54 | o added ice file raw data for vignette
55 |
56 | VERSION 0.0.2
57 |
58 | o added tiles option to graticule to return polygons
59 |
60 | o added a readme for the GitHub front page
61 |
62 | VERSION 0.0.1
63 |
64 | o basics working
65 |
66 |
--------------------------------------------------------------------------------
/R/lonlat.R:
--------------------------------------------------------------------------------
1 | #' Add longitude latitude lines to a plot
2 | #'
3 | #' Use the coordinates of the input raster to generate coordinate rasters, these are
4 | #' then used in a contour plot.
5 | #'
6 | #' Plot is added to an existing plot by default.
7 | #'
8 | #' @param x input raster
9 | #' @param na.rm logical, remove missing values from generated coordinates
10 | #' @param ... passed to [graphics::contour()]
11 | #' @param plot logical, plot the result
12 | #' @param add logical, add to current plot or instantiate one
13 | #' @param lon if TRUE, only longitude plotted
14 | #' @param lat if TRUE (and `lon = FALSE`) only latitude plotted
15 | #'
16 | #' @return RasterBrick of the longitude and latitude values, two layers
17 | #' @export
18 | #' @return (invisibly) the raster (RasterBrick) object with longitude and latitude values of the input
19 | #' as two layers, otherwise this function used for side-effect (drawing on a plot)
20 | #' @importFrom graphics contour
21 | #' @examples
22 | #' plot(c(-180, 180), c(-90, 90))
23 | #' lonlat(raster::raster())
24 | #'
25 | #' p <- raster::projectExtent(raster::raster(), "+proj=igh")
26 | #' lonlat(p, add = FALSE)
27 | #' lonlat(p, levels = seq(-180, 180, by =15), add = FALSE)
28 | #'
29 | #' lonlat(p, levels = seq(-180, 180, by = 5), add = FALSE, lon = TRUE)
30 | #' lonlat(p, levels = seq(-180, 180, by = 15), add = TRUE, lat = TRUE)
31 | lonlat <- function(x, na.rm = FALSE, lon = FALSE, lat = FALSE, ..., plot = TRUE, add = TRUE) {
32 | x <- x[[1]]
33 | ll <- FALSE
34 | if (is.na(raster::projection(x))) {
35 | message("no projection metadata on raster, assuming longlat")
36 | ll <- TRUE
37 | }
38 | cell <- seq_len(raster::ncell(x))
39 | if (na.rm) cell <- cell[!is.na(raster::values(x))]
40 | lons <- raster::raster(x)
41 | lats <- raster::raster(x)
42 | if (ll) {
43 | lons[cell] <- raster::xFromCell(x, cell)
44 | lats[cell] <- raster::yFromCell(x, cell)
45 | } else {
46 | xy <- raster::xyFromCell(x, cell)
47 |
48 | suppressWarnings( xy <- reproj::reproj_xy(xy, lonlatp4(), source = raster::projection(x)))
49 | lons[cell] <- xy[,1]
50 | lats[cell] <- xy[,2]
51 | }
52 | if (plot) {
53 | if (lon || !lat) raster::contour(lons, add = add, ...)
54 | if (!lon) raster::contour(lats, add = TRUE, ...)
55 | }
56 | invisible(raster::brick(lons, lats))
57 | }
58 |
--------------------------------------------------------------------------------
/R/graticule2.R:
--------------------------------------------------------------------------------
1 | #' @noRd
2 | #' @examples
3 | #' lon <- seq(-40, 40, by = 10)
4 | #' lat <- seq( -60, -40, by = 5)
5 | #' g <- graticule_tiles(lon, lat, margin = TRUE)
6 | #' xx <- seq(-90, 90, length = 10) + 147
7 | #' yy <- seq(-90, 90, length = 5)
8 | #' g <- graticule_tiles(xx, yy, proj = "+proj=ortho +lon_0=147 +ellps=WGS84")
9 | #' plot(g, col = c("black", "grey"))
10 | #' prj <- "+proj=stere +lat_0=-90 +lat_ts=-70 +lon_0=0 +k=1 +x_0=0 +y_0=0 +a=6378273 +b=6356889.449 +units=m +no_defs"
11 | #' meridians <- seq(-180, 160, by = 20)
12 | #' parallels <- c(-80, -73.77, -68, -55, -45)
13 | #' polargrid <- graticule_tiles(lons = c(meridians, 180),
14 | #' lats = parallels, proj = prj)
15 | NULL
16 |
17 | #' Graticule tiles
18 | #' @noRd
19 | #'
20 | #' @importFrom stats approx
21 | #' @importFrom utils head
22 | graticule_tiles <- function(lons = seq(-180, 180, by = 15), lats = seq(-84, 84, by = 12),
23 | nverts = 24, proj = NULL,
24 | margin = FALSE) {
25 | if (length(lons) < 2) stop("length of argument `lons` is < 2")
26 | if (length(lats) < 2) stop("length of argument `lats` is < 2")
27 | grid <- raster::raster(raster::extent(range(lons), range(lats)),
28 | ncols = length(lons)-1, nrow = length(lats)-1)
29 |
30 |
31 | if (margin) {
32 |
33 | cells <-
34 | c(raster::cellFromRow(grid, 1), ## top margin
35 | raster::cellFromCol(grid, ncol(grid))[-c(1,nrow(grid))], ## right margin
36 | utils::head(rev(raster::cellFromRow(grid, nrow(grid))), -1), ## bottom margin
37 | utils::head(rev(raster::cellFromCol(grid, 1)), -1)) ## left margin
38 | } else {
39 | cells <- seq_len(raster::ncell(grid))
40 | }
41 |
42 | ll <- vector("list", length(cells))
43 | ## loop extents of every pixel (I know, I know)
44 | p4 <- lonlatp4()
45 | for (i in seq_along(ll)) {
46 | ex <- raster::extentFromCells(grid, cells[i])
47 | m1 <- ll_extent(c(ex@xmin, ex@xmax), c(ex@ymin, ex@ymax))
48 | if (!is.null(proj)) {
49 | m1 <- reproj::reproj_xy(m1, proj, source = lonlatp4())
50 | p4 <- proj
51 | }
52 | ll[[i]] <- m1
53 | }
54 | xx <- do.call(raster::spPolygons, ll)
55 |
56 | xx <- sp::SpatialPolygonsDataFrame(xx, data.frame(x = 1:length(xx)))
57 | raster::projection(xx) <- p4
58 | xx
59 | }
60 |
61 |
62 | ll_extent <- function(lonrange, latrange, nverts = 24, mindist = 1e5) {
63 | ## technically we don't need meridionally segmentation, but this is general enough
64 | ## for any set of segments assumed to be rhumb lines
65 | dat2 <- matrix(c(lonrange,
66 | latrange)[c(1, 2, 2, 2, 2, 1, 1, 1,
67 | 3, 3, 3, 4, 4, 4, 4, 3)],
68 | ncol = 2)
69 | l <- lapply(seq(1, nrow(dat2), by = 2),
70 | function(.x) {
71 | xy <- dat2[c(.x, .x + 1), ]
72 | suppressWarnings(dst <- geosphere::distRhumb(xy[1, ], xy[2, ]))
73 | nn <- if (!is.null(nverts)) nverts else round(dst/mindist)
74 | nn <- max(c(nn, 3)) ## there's got to be a limit
75 | cbind(stats::approx(xy[,1], n = nn)$y, approx(xy[,2], n = nn)$y)
76 | })
77 | do.call(rbind, lapply(l, head, -1))
78 | }
79 |
--------------------------------------------------------------------------------
/README.Rmd:
--------------------------------------------------------------------------------
1 | ---
2 | output: github_document
3 | editor_options:
4 | chunk_output_type: console
5 | ---
6 |
7 |
8 |
9 | ```{r, include = FALSE}
10 | knitr::opts_chunk$set(
11 | collapse = TRUE,
12 | comment = "#>",
13 | fig.path = "man/figures/README-",
14 | out.width = "100%"
15 | )
16 | ```
17 | # graticule
18 |
19 |
20 |
21 | [](https://CRAN.R-project.org/package=graticule)
22 | [](https://github.com/hypertidy/graticule/actions/workflows/R-CMD-check.yaml)
23 |
24 |
25 |
26 | Graticules are the longitude latitude lines shown on a projected map, and defining and drawing these lines is not easy to automate. The graticule package provides the tools to create and draw these lines by explicit specification by the user. This provides a good compromise between high-level automation and the flexibility to drive the low level details as needed, using base graphics in R.
27 |
28 |
29 | ## Installation
30 |
31 | You can install the released version of graticule from [CRAN](https://CRAN.R-project.org) with:
32 |
33 | ``` r
34 | install.packages("graticule")
35 | ```
36 |
37 | And the development version from [GitHub](https://github.com/) with:
38 |
39 | ``` r
40 | # install.packages("devtools")
41 | devtools::install_github("hypertidy/graticule")
42 | ```
43 | ## Example
44 |
45 | This is a basic example which shows how to create a graticule at specific longitude and latitude spacings
46 | and in a given projection.
47 |
48 | ```{r example}
49 | library(graticule)
50 | grat <- graticule(lons = seq(100, 220, by = 15), lats = seq(-60, -10, by = 5), proj = "+proj=laea +lon_0=140 +lat_0=-90 +datum=WGS84")
51 | plot(grat)
52 | ```
53 |
54 | There is an automatic segmentation that is done at equal distances along these [rhumb lines](https://en.wikipedia.org/wiki/Rhumb_line). This is not an *[ideal spacing](https://bost.ocks.org/mike/example/)* but is an improvement on the common alternatives, and is easier to work with when you need fine control.
55 |
56 | If your projection is not wildly warped in most areas then the default rhumb line segmentation is the best first step.
57 |
58 | ```{r rhumb-points}
59 | plot(as(grat, "SpatialPoints"))
60 | ```
61 |
62 | This also allows the common case of creating a sensible single polygon *wedge*, i.e.
63 |
64 | ```{r wedge}
65 | wedge <- graticule(lons = c(-40, 40), lats = c(-60, -40), proj = "+proj=laea +lat_0=-50 +lon_0=0 +x_0=0 +y_0=0 +datum=WGS84 +units=m +no_defs")
66 | plot(wedge)
67 | points(as(wedge, "SpatialPoints"))
68 | ```
69 |
70 | ## Quick and dirty plot
71 |
72 | Give it a raster and let it plot, can provide `levels` as per contour (values of longitude or latitude to draw as lines), and separate longitude or latitude plot on their own.
73 |
74 | Sometimes it's enough, sometimes we can muck around to get what we want.
75 |
76 | ```{r lonlat}
77 | tfile <- system.file("extdata", "nt_20140320_f17_v01_s.bin", package = "graticule", mustWork = TRUE)
78 | ice <- raster::raster(tfile)
79 | ice[!ice > 0] <- NA
80 | raster::plot(ice, col = palr::ice_pal(100))
81 | lonlat(ice)
82 |
83 | raster::plot(ice, col = palr::ice_pal(100))
84 | lonlat(ice, lon = TRUE, levels = seq(-180, 165, by = 15))
85 | lonlat(ice, lat = TRUE, levels = seq(-85, -40, by = 5))
86 |
87 | ## not much good so let's get the actual arrays
88 | lon <- lonlat(ice, plot = FALSE)[[1]]
89 | lat <- lonlat(ice, plot = FALSE)[[2]]
90 | lon[lat < -85] <- NA
91 | raster::plot(ice, col = palr::ice_pal(100))
92 | raster::contour(lon, add = TRUE)
93 | lonlat(ice, lat = TRUE, levels = seq(-85, -40, by = 5))
94 |
95 |
96 | ```
97 |
98 | ## Known Issues
99 |
100 | Please feel free to share your experiences and report problems at https://github.com/hypertidy/graticule/issues
101 |
102 | * general problems with segmentation, this is not done smartly yet (see hypertidy/bigcurve or the s2 package)
103 | * There's work needed for when `graticule_labels()` are created without using `xline/yline`, need more careful separation between generating every combination in the grid versus single lines
104 |
105 |
106 | ---
107 |
108 | Please note that the 'graticule' project is released with a
109 | [Contributor Code of Conduct](https://github.com/hypertidy/graticule/blob/master/CODE_OF_CONDUCT.md).
110 | By contributing to this project, you agree to abide by its terms.
111 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | # graticule
5 |
6 |
7 |
8 | [](https://CRAN.R-project.org/package=graticule)
10 | [](https://github.com/hypertidy/graticule/actions/workflows/R-CMD-check.yaml)
11 |
12 |
13 | Graticules are the longitude latitude lines shown on a projected map,
14 | and defining and drawing these lines is not easy to automate. The
15 | graticule package provides the tools to create and draw these lines by
16 | explicit specification by the user. This provides a good compromise
17 | between high-level automation and the flexibility to drive the low level
18 | details as needed, using base graphics in R.
19 |
20 | ## Installation
21 |
22 | You can install the released version of graticule from
23 | [CRAN](https://CRAN.R-project.org) with:
24 |
25 | ``` r
26 | install.packages("graticule")
27 | ```
28 |
29 | And the development version from [GitHub](https://github.com/) with:
30 |
31 | ``` r
32 | # install.packages("devtools")
33 | devtools::install_github("hypertidy/graticule")
34 | ```
35 |
36 | ## Example
37 |
38 | This is a basic example which shows how to create a graticule at
39 | specific longitude and latitude spacings and in a given projection.
40 |
41 | ``` r
42 | library(graticule)
43 | #> Loading required package: sp
44 | grat <- graticule(lons = seq(100, 220, by = 15), lats = seq(-60, -10, by = 5), proj = "+proj=laea +lon_0=140 +lat_0=-90 +datum=WGS84")
45 | plot(grat)
46 | ```
47 |
48 |
49 |
50 | There is an automatic segmentation that is done at equal distances along
51 | these [rhumb lines](https://en.wikipedia.org/wiki/Rhumb_line). This is
52 | not an *[ideal spacing](https://bost.ocks.org/mike/example/)* but is an
53 | improvement on the common alternatives, and is easier to work with when
54 | you need fine control.
55 |
56 | If your projection is not wildly warped in most areas then the default
57 | rhumb line segmentation is the best first step.
58 |
59 | ``` r
60 | plot(as(grat, "SpatialPoints"))
61 | ```
62 |
63 |
64 |
65 | This also allows the common case of creating a sensible single polygon
66 | *wedge*, i.e.
67 |
68 | ``` r
69 | wedge <- graticule(lons = c(-40, 40), lats = c(-60, -40), proj = "+proj=laea +lat_0=-50 +lon_0=0 +x_0=0 +y_0=0 +datum=WGS84 +units=m +no_defs")
70 | plot(wedge)
71 | points(as(wedge, "SpatialPoints"))
72 | ```
73 |
74 |
75 |
76 | ## Quick and dirty plot
77 |
78 | Give it a raster and let it plot, can provide `levels` as per contour
79 | (values of longitude or latitude to draw as lines), and separate
80 | longitude or latitude plot on their own.
81 |
82 | Sometimes it’s enough, sometimes we can muck around to get what we want.
83 |
84 | ``` r
85 | tfile <- system.file("extdata", "nt_20140320_f17_v01_s.bin", package = "graticule", mustWork = TRUE)
86 | ice <- raster::raster(tfile)
87 | ice[!ice > 0] <- NA
88 | raster::plot(ice, col = palr::ice_pal(100))
89 | lonlat(ice)
90 | ```
91 |
92 |
93 |
94 | ``` r
95 |
96 | raster::plot(ice, col = palr::ice_pal(100))
97 | lonlat(ice, lon = TRUE, levels = seq(-180, 165, by = 15))
98 | lonlat(ice, lat = TRUE, levels = seq(-85, -40, by = 5))
99 | ```
100 |
101 |
102 |
103 | ``` r
104 |
105 | ## not much good so let's get the actual arrays
106 | lon <- lonlat(ice, plot = FALSE)[[1]]
107 | lat <- lonlat(ice, plot = FALSE)[[2]]
108 | lon[lat < -85] <- NA
109 | raster::plot(ice, col = palr::ice_pal(100))
110 | raster::contour(lon, add = TRUE)
111 | lonlat(ice, lat = TRUE, levels = seq(-85, -40, by = 5))
112 | ```
113 |
114 |
115 |
116 | ## Known Issues
117 |
118 | Please feel free to share your experiences and report problems at
119 |
120 |
121 | - general problems with segmentation, this is not done smartly yet
122 | (see hypertidy/bigcurve or the s2 package)
123 | - There’s work needed for when `graticule_labels()` are created
124 | without using `xline/yline`, need more careful separation between
125 | generating every combination in the grid versus single lines
126 |
127 | ------------------------------------------------------------------------
128 |
129 | Please note that the ‘graticule’ project is released with a [Contributor
130 | Code of
131 | Conduct](https://github.com/hypertidy/graticule/blob/master/CODE_OF_CONDUCT.md).
132 | By contributing to this project, you agree to abide by its terms.
133 |
--------------------------------------------------------------------------------
/R/graticule.R:
--------------------------------------------------------------------------------
1 | #' graticule: graticule lines for maps
2 | #'
3 | #' @docType package
4 | #' @name graticule
5 | NULL
6 | limfun <- function(x, lim, meridian = TRUE, nverts = NULL) {
7 |
8 | mindist <- getOption("graticule.mindist")
9 | if (is.na(mindist) || is.null(mindist) || !is.numeric(mindist)) {
10 | mindist <- 5e4
11 | warning(sprintf("option('graticule.mindist') is malformed, using %fm", mindist))
12 | }
13 | if (!meridian) {
14 | out <- ll_extent(lim, c(x, x), mindist = mindist, nverts = nverts)
15 | } else {
16 | out <- ll_extent(c(x, x), lim, mindist = mindist, nverts = nverts)
17 | }
18 | out
19 | }
20 |
21 | step_fun <- function(x, steps, nd = 60, meridian = TRUE) {
22 | ind <- 1:2
23 |
24 | if (!meridian) ind <- 2:1
25 | op <- options(warn = -1)
26 | on.exit(options(op))
27 | step_seg <- as.data.frame(utils::head(matrix(steps, nrow = length(steps)+1, ncol = 2), -2))
28 | lapply(split(step_seg, 1:nrow(step_seg)), function(a) cbind(x = x, y = seq(unlist(a)[1], unlist(a)[2], length = nd))[, ind])
29 | }
30 |
31 | buildlines <- function(x) {
32 | do.call("rbind", lapply(seq_along(x), function(xx) {
33 | res <- data.frame(x[[xx]], rep(xx, nrow(x[[xx]])))
34 | names(res) <- c("x", "y", "id")
35 | res
36 | }))
37 | }
38 |
39 | ## from raster findMethods("isLonLat")[["character"]]
40 | # isLonLat <- function (x)
41 | # {
42 | # res1 <- grep("longlat", as.character(x), fixed = TRUE)
43 | # res2 <- grep("lonlat", as.character(x), fixed = TRUE)
44 | # if (length(res1) == 0L && length(res2) == 0L) {
45 | # return(FALSE)
46 | # }
47 | # else {
48 | # return(TRUE)
49 | # }
50 | # }
51 |
52 | lonlatp4 <- function() {
53 | "+proj=longlat +datum=WGS84"
54 | }
55 |
56 | #' Create graticule lines.
57 | #'
58 | #' Specify the creation of lines along meridians by specifying their placement
59 | #' at particular \code{lons} (longitudes) and \code{lats} (latitudes) and their extents
60 | #' with \code{xlim} (extent of parallel line in longitude) and \code{ylim} (extent of meridional line in latitude).
61 | #'
62 | #' Provide a valid PROJ.4 string to return the graticule lines in this projection. If this is not specified the graticule
63 | #' lines are returned in their original longlat / WGS84.
64 | #' All segments are discretized as _rhumb_lines_ at `getOption("graticule.mindist")` metres, which
65 | #' defaults to `5e4`.
66 | #' The arguments \code{xlim}, \code{ylim} and \code{nverts} are ignored if \code{tiles} is \code{TRUE}.
67 | #' @param lons longitudes for meridional lines
68 | #' @param lats latitudes for parallel lines
69 | #' @param nverts number of discrete vertices for each segment
70 | #' @param xlim maximum range of parallel lines
71 | #' @param ylim maximum range of meridional lines
72 | #' @param proj optional proj.4 string for output object
73 | #' @param tiles if \code{TRUE} return polygons as output
74 | #' @return SpatialLines or SpatialPolygons object
75 | #' @export
76 | #' @importFrom reproj reproj_xy
77 | #' @importFrom raster isLonLat raster extent values<- ncell res
78 | #' @importFrom sp SpatialLinesDataFrame Line Lines SpatialLines CRS
79 | #' @examples
80 | #' graticule()
81 | graticule <- function(lons, lats, nverts = NULL, xlim, ylim, proj = NULL, tiles = FALSE) {
82 | if (is.null(proj)) proj <- lonlatp4()
83 | proj <- as.character(proj) ## in case we are given CRS
84 | trans <- FALSE
85 | if (tiles) {
86 | if (!missing(xlim)) {
87 | warning("xlim is ignored if 'tiles = TRUE'")
88 | }
89 | if (!missing(ylim)) {
90 | warning("ylim is ignored if 'tiles = TRUE'")
91 | }
92 |
93 | }
94 | if (!raster::isLonLat(proj)) trans <- TRUE
95 | if (missing(lons)) {
96 | #usr <- par("usr")
97 | #if (all(usr == c(0, 1, 0, 1))) {
98 | lons <- seq(-180, 180, by = 15)
99 | }
100 | if (missing(lats)) {
101 | lats <- seq(-90, 90, by = 10)
102 | }
103 |
104 | if (tiles) {
105 | pp <- graticule_tiles(lons, lats, proj = proj)
106 | return(pp)
107 | }
108 | if (missing(xlim)) xlim <- range(lons)
109 | if (missing(ylim)) ylim <- range(lats)
110 | xline <- lapply(lons, limfun, lim = ylim, meridian = TRUE, nverts = nverts)
111 | yline <- lapply(lats, limfun, lim = xlim, meridian = FALSE, nverts = nverts)
112 | xs <- buildlines(xline)
113 | ys <- buildlines(yline)
114 | ys$id <- ys$id + max(xs$id)
115 | xs$type <- "meridian"
116 | ys$type <- "parallel"
117 | d <- rbind(xs, ys)
118 | d0 <- split(d, d$id)
119 | l <- vector("list", length(d0))
120 | for (i in seq_along(d0)) {
121 | m1 <- as.matrix(d0[[i]][, c("x", "y")])
122 | if (trans) {
123 | m1 <- reproj::reproj_xy(m1, proj, source = "+proj=longlat +datum=WGS84")
124 | } else {
125 | proj <- "OGC:CRS84"
126 | }
127 | l1 <- sp::Lines(list(sp::Line(m1)), ID = as.character(i))
128 | l[[i]] <- l1
129 | }
130 | l <- sp::SpatialLinesDataFrame(sp::SpatialLines(l, proj4string = sp::CRS(proj)),
131 | data.frame(ID = as.character(seq_along(l))))
132 | l
133 |
134 | }
135 |
136 | #' Create graticule labels.
137 | #'
138 | #' Returns a set of points with labels, for plotting in conjuction with \code{\link{graticule}}.
139 | #'
140 | #' SpatialPoints are returned in the projection of \code{proj} if given, or longlat / WGS84.
141 | #' @param lons longitudes for meridional labels
142 | #' @param lats latitudes for parallel labels
143 | #' @param xline meridian/s for placement of parallel labels
144 | #' @param yline parallel/s for placement of meridian labels
145 | #' @param proj optional proj.4 string for output object
146 | #' @export
147 | #' @importFrom sp degreeLabelsEW degreeLabelsNS coordinates<- proj4string<-
148 | #' @return SpatialPoints object with labels for downstream use
149 | #' @examples
150 | #' xx <- c(100, 120, 160, 180)
151 | #' yy <- c(-80,-70,-60, -50,-45, -30)
152 | #' prj <- "+proj=lcc +lon_0=150 +lat_0=-80 +lat_1=-85 +lat_2=-75 +ellps=WGS84"
153 | #' plot(graticule(lons = xx, lats = yy, proj = prj))
154 | #' labs <- graticule_labels(lons = xx, lats = yy, xline = 100, yline = -80, proj = prj)
155 | #' op <- par(xpd = NA)
156 | #' text(labs, lab = parse(text = labs$lab), pos = c(2, 1)[labs$islon + 1], adj = 1.2)
157 | #' par(op)
158 | graticule_labels <- function(lons, lats, xline, yline, proj = NULL) {
159 | if (is.null(proj)) proj <- lonlatp4()
160 | proj <- as.character(proj) ## in case we are given CRS
161 | trans <- FALSE
162 | if (!raster::isLonLat(proj)) trans <- TRUE
163 | if (missing(lons)) {
164 | #usr <- par("usr")
165 | #if (all(usr == c(0, 1, 0, 1))) {
166 | lons <- seq(-180, 180, by = 15)
167 | }
168 | if (missing(lats)) {
169 | lats <- seq(-90, 90, by = 10)
170 | }
171 | if (missing(xline)) xline <- lons
172 | if (missing(yline)) yline <- lats
173 |
174 | lonlabs <- expand.grid(x = lons, y = yline)
175 | lonlabs$lab <- degreeLabelsEW(lonlabs$x)
176 | lonlabs$islon <- TRUE
177 | latlabs <- expand.grid(x = xline, y = lats)
178 | latlabs$lab <- degreeLabelsNS(latlabs$y)
179 | latlabs$islon <- FALSE
180 | l <- rbind(lonlabs, latlabs)
181 | p4 <- lonlatp4()
182 | if (trans) {
183 | l[1:2] <- reproj::reproj_xy(l[1:2], proj, source = p4)
184 | p4 <- proj
185 | }
186 |
187 | coordinates(l) <- 1:2
188 |
189 |
190 | proj4string(l) <- CRS(p4)
191 |
192 | l
193 | }
194 |
--------------------------------------------------------------------------------
/vignettes/graticule.Rmd:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Graticule"
3 | author: "Michael D. Sumner"
4 | date: "`r Sys.Date()`"
5 | output:
6 | rmarkdown::html_vignette:
7 | fig_width: 9
8 | fig_height: 9
9 | vignette: >
10 | %\VignetteIndexEntry{Graticule}
11 | \usepackage[utf8]{inputenc}
12 | %\VignetteEngine{knitr::rmarkdown}
13 | editor_options:
14 | chunk_output_type: console
15 | ---
16 |
17 |
18 |
19 |
20 | ```{r, echo = FALSE}
21 | knitr::opts_chunk$set(
22 | collapse = TRUE,
23 | comment = "#>",
24 | fig.path = "README-"
25 | )
26 |
27 |
28 | ```
29 |
30 | # Graticule
31 |
32 | Graticules are the longitude latitude lines shown on a projected map, and defining and drawing these lines is not easy to automate. The graticule package provides the tools to
33 | create and draw these lines by explicit specification by the user. This provides a good compromise between high-level automation and the flexibility to drive the low level details as needed, using base graphics in R.
34 |
35 |
36 | Please note that this is an evolving topic, across a number of packages in R. There's no
37 | ongoing integration of how best to do this, and some of the commentary in this vignette
38 | will be out of date quickly as individual packages do updates. I've recorded the exact versions used for this document at the end.
39 |
40 | This is an area that needs much more discussion and outlining of needs and experiences.
41 |
42 |
43 | # Examples
44 |
45 |
46 | A simple example to build a map around the state of Victoria in Australia. Victoria uses a local Lambert Conformal Conic projection that was introduced while the shift to GDA94 was implemented, to reduce complications due to working with more than one UTM zone for the state.
47 |
48 | ```{r,message=FALSE, eval=FALSE}
49 | library(raster)
50 |
51 | library(graticule)
52 |
53 | ## VicGrid
54 | prj <- "+proj=lcc +lat_1=-36 +lat_2=-38 +lat_0=-37 +lon_0=145 +x_0=2500000 +y_0=2500000 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs"
55 |
56 |
57 | ## specify exactly where we want meridians and parallels
58 | lons <- seq(140, 150, length = 5)
59 | lats <- seq(-40, -35, length = 6)
60 | ## optionally, specify the extents of the meridians and parallels
61 | ## here we push them out a little on each side
62 | xl <- range(lons) + c(-0.4, 0.4)
63 | yl <- range(lats) + c(-0.4, 0.4)
64 | ## build the lines with our precise locations and ranges
65 | grat <- graticule(lons, lats, proj = prj, xlim = xl, ylim = yl)
66 | ## build the labels, here they sit exactly on the western and northern extent
67 | ## of our line ranges
68 | labs <- graticule_labels(lons, lats, xline = min(xl), yline = max(yl), proj = prj)
69 |
70 | ## set up a map extent and plot
71 | op <- par(mar = rep(0, 4))
72 | plot(extent(grat) + c(4, 2) * 1e5, asp = 1, type = "n", axes = FALSE, xlab = "", ylab = "")
73 | #plot(pmap, add = TRUE)
74 | ## the lines are a SpatialLinesDataFrame
75 | plot(grat, add = TRUE, lty = 5, col = rgb(0, 0, 0, 0.8))
76 | ## the labels are a SpatialPointsDataFrame, and islon tells us which kind
77 | text(subset(labs, labs$islon), lab = parse(text = labs$lab[labs$islon]), pos = 3)
78 | text(subset(labs, !labs$islon), lab = parse(text = labs$lab[!labs$islon]), pos = 2)
79 | par(op)
80 |
81 | ```
82 |
83 |
84 | ## A polar example
85 |
86 | Download some sea ice concentration data and plot with a graticule. These passive microwave data are defined on a Polar Stereographic grid on the Hughes ellipsoid (predating WGS84), and there are daily files available since 1978. This is not the prettiest map, but the example is showing how we have control over exactly where the lines are created. We can build the lines anywhere, not necessarily at regular intervals or rounded numbers, and we can over or under extend the parallels relative to the meridians and vice versa.
87 |
88 | ```{r,message=FALSE}
89 | library(raster)
90 | library(graticule)
91 |
92 | tfile <- system.file("extdata", "nt_20140320_f17_v01_s.bin", package = "graticule")
93 | ice <- raster(tfile)
94 |
95 | meridians <- seq(-180, 160, by = 20)
96 | parallels <- c(-80, -73.77, -68, -55, -45)
97 | mlim <- c(-180, 180)
98 | plim <- c(-88, -50)
99 | grat <- graticule(lons = meridians, lats = parallels, xlim = mlim, ylim = plim, proj = projection(ice))
100 | labs <- graticule_labels(meridians, parallels, xline = -45, yline = -60, proj = projection(ice))
101 | plot(ice, axes = FALSE)
102 | plot(grat, add = TRUE, lty = 3)
103 | text(labs, lab = parse(text= labs$lab), col= c("firebrick", "darkblue")[labs$islon + 1], cex = 0.85)
104 | title(sprintf("Sea ice concentration %s", gsub(".bin", "", basename(tfile))), cex.main = 0.8)
105 | title(sub = projection(ice), cex.sub = 0.6)
106 | ```
107 |
108 | ## Create the graticule as polygons
109 |
110 | Continuing from the sea ice example, build the graticule grid as actual polygons. Necessarily the `xlim/ylim` option is ignored since we have not specified sensibly closed polygonal rings where there are under or over laps.
111 |
112 | ```{r}
113 | polargrid <- graticule(lons = c(meridians, 180), lats = parallels, proj = projection(ice), tiles = TRUE)
114 | centroids <- reproj::reproj_xy(coordinates(polargrid), "+proj=longlat +datum=WGS84", source = projection(ice))
115 | labs <- graticule_labels(meridians, parallels, proj = projection(ice))
116 | #labs <- graticule_labels(as.integer(centroids[,1]), as.integer(centroids[,2]), proj = projection(ice))
117 | #labs <- labs[!duplicated(as.data.frame(labs)), ] ## this needs a fix
118 | cols <- sample(colors(), nrow(polargrid))
119 | op <- par(mar = rep(0, 4))
120 | plot(polargrid, col = cols, bg = "black")
121 | text(labs[labs$islon, ], lab = parse(text = labs$lab[labs$islon]), col = "white", cex = 0.9, pos = 3)
122 | par(op)
123 |
124 | ```
125 | ## Comparison to tools in sp and rgdal
126 |
127 | **This section kept for historical interest only. **
128 |
129 | See: https://github.com/hypertidy/graticule/issues/19#issuecomment-1379874409
130 |
131 |
132 |
133 | Also see here for another implementation of the Tissot Indicatrix in R by user [whuber on GIS StackExchange](https://gis.stackexchange.com/questions/31651/an-example-tissot-ellipse-for-an-equirectangular-projection). This is available in the [dev package tissot](https://github.com/hypertidy/tissot).
134 |
135 |
136 | ## Notes
137 |
138 | Efforts could be shared with the sp and rgdal projects to improve the functionality for the `llgridlines` and its worker functions `gridlines` and `gridat` in that central place, and I agree with this. But I have an interest in working with graticules more directly as objects, and potentially stored in relational-table approach built on dplyr, and so I just found it simpler to start from scratch in this package. Also, there is a lot of this functionality spread around the place in sp, raster, maptools, fields, oce and many others. It is time for a new review, analogous to the effort that built sp in ca. 2002.
139 |
140 | ### Terminology
141 |
142 | I tend to use the same terminology as used within [Manifold System](https://manifold.net/) *because it's so awesome* and that's where I first learnt about most of these concepts. In my experience not many people use the term *graticule* in this way, so take it from the master himself on page 8 (Snyder, 1987):
143 |
144 | > To identify the location of points on the Earth, a graticule or network of longitude
145 | > and latitude lines has been superimposed on the surface. They are commonly
146 | > referred to as meridians and parallels, respectively.
147 |
148 |
149 | ## References
150 |
151 | [Snyder, John Parr. Map projections--A working manual. No. 1395. USGPO, 1987.](https://pubs.er.usgs.gov/publication/pp1395)
152 |
153 | ## Environment
154 |
155 | ```{r}
156 | sessioninfo::session_info()
157 | ```
158 |
159 |
--------------------------------------------------------------------------------