oce icon indicating copy to clipboard operation
oce copied to clipboard

mapPlot() should offer more control of axis labels

Open dankelley opened this issue 5 years ago • 15 comments

Following up on discussion at #1706, maybe mapPlot() ought to have a new argument for controlling the labelling of axes.

it could be called axisStyle, and take numeric values, perhaps

  • [x] 0 for signed numbers without labels
  • [x] 1 (the default) for unsigned numbers followed by letters that indicate the hemisphere
  • [x] 2 for signed numbers with a degree symbol to the right
  • [x] 3 for unsigned numbers with a degree symbol to the right

with other varieties possibly coming later, perhaps with degrees and symbols (which might be quite ugly...)

dankelley avatar Jun 11 '20 18:06 dankelley

I have this working in "develop" (see ?mapPlot for the numerical codes and explanation, which supersedes what I wrote in the previous comment), and the test code

library(oce)
data("coastlineWorld")
proj <- "+proj=merc"
lonlim <- c(-20, 20)
latlim <- c(-20, 20)
if (!interactive()) png("1707a.png")
par(mfrow=c(2, 2), mar=c(2,2,1,1))
for (axisStyle in 1:4) {
    mapPlot(coastlineWorld, longitudelim=lonlim, latitudelim=latlim,
            col="tan", proj=proj, axisStyle=axisStyle, grid=c(5,5))
    mtext(paste0("axisStyle=", axisStyle), cex=par("cex"))
}
if (!interactive()) dev.off()

produces as below.

1707a

dankelley avatar Jun 12 '20 13:06 dankelley

@clayton33 since this stems from your #1706 issue, can I ask you to look at two things and comment here?

  1. Does the above look OK? (Note that the test code is also in https://github.com/dankelley/oce-issues/tree/master/17xx/1707, which I will update as appropriate)

  2. Do you want me to make an additional style, axisStyle=5 maybe, that has unsigned numbers followed by both a degree sign and a hemisphere letter? It would be just 5min of work, and I'm inclined to do it but I just didn't do it because you mentioned at #1706 that you thought that would be ugly

dankelley avatar Jun 12 '20 13:06 dankelley

  1. Yes, looks very very nice.
  2. Looking at it I'm inclined to include the additional style. Those who might doing it in two languages, might choose axisStyle = 3 or axisStyle = 1 for niceness, but others might want the degree and hemisphere, for say English.

clayton33 avatar Jun 12 '20 13:06 clayton33

@clayton33 I added style 5, and I actually like it. What do you think?

library(oce)
data("coastlineWorld")
proj <- "+proj=merc"
lonlim <- c(-20, 20)
latlim <- c(-20, 20)
if (!interactive()) png("1707a.png", width=7, height=5, res=120, unit="in")
par(mfrow=c(2, 3), mar=c(2,2,1,1))
for (axisStyle in 1:5) {
    mapPlot(coastlineWorld, longitudelim=lonlim, latitudelim=latlim,
            col="tan", proj=proj, axisStyle=axisStyle, grid=c(5,5))
    mtext(paste0("axisStyle=", axisStyle), cex=par("cex"))
}
plot(0:1,0:1,xlab="",ylab="",axes=FALSE,type="n")
box()
text(0.5,0.5,"Test of\nmapPlot(...,axisType)\n(github issue 1707)")
if (!interactive()) dev.off()

1707a

dankelley avatar Jun 12 '20 14:06 dankelley

Looks nice :)

clayton33 avatar Jun 12 '20 14:06 clayton33

Closing now. Thanks, @clayton33

dankelley avatar Jun 12 '20 14:06 dankelley

Argument las from graphics::axis() please!

chrisdane avatar Aug 19 '24 11:08 chrisdane

@chrisdane I'll take a look. I reopened the issue because open issues are a to-do list.

NOTE: this won't make it to CRAN for 6 months to a year. We have a release that is being finalized on CRAN right now, and there are rules about frequent updates.

dankelley avatar Aug 19 '24 11:08 dankelley

I'll make it accept las as either a 1-element or 2-element vector. Otherwise we'll get some odd things, like below. (I've never played with las before ... I like the default, which uses less margin space than if we have horizontal labels on the vertical axis.)

# Demo of why I will set up for las to be a 2-element vector,
# if the user wants more control.
library(oce)
data(coastlineWorld)
par(mfrow = c(2, 2))
for (las in 0:3) {
    mapPlot(coastlineCut(coastlineWorld, -100),
        longitudelim = c(-130, -55), latitudelim = c(35, 60),
        las = las,
        projection = "+proj=lcc +lat_0=30 +lat_1=60 +lon_0=-100", col = "gray"
    )
    mtext(sprintf("with las = %d", las), side = 3)
}

image

dankelley avatar Aug 19 '24 13:08 dankelley

Actually, on second thought, I am going to make it require a two-element vector. Otherwise the user might get odd results like the bottom two panels in the plot above. Making it a two-element vector lets me write the docs to explain things, as provisionally below (in Roxygen2 format).

#' @param las two-element axis label orientation, passed to [axis()]. The first
#' value is for the horizontal axis, and the second is for the vertical axis.
#' See [par()] for the meanings of the permitted values, 0, 1, 2 and 3.

dankelley avatar Aug 19 '24 14:08 dankelley

This shows the main part of the diff (not pushed to GH ... this is just a note to myself, really).

grep -n "axis(side = " map.R
657:                    axis(side = 1, at = at, label = formatLonLat(longitude, "longitude", axisStyle = axisStyle), las = las[1])
679:                    axis(side = 2, at = at, label = formatLonLat(latitude, "latitude", axisStyle = axisStyle), las = las[2], line = line)
2558:                                axis(side = 1, at = axisLabels1$at[!skip], labels = FALSE, mgp = mgp)
2560:                                axis(side = 1, at = axisLabels1$at[!skip], labels = axisLabels1$value[!skip], las = las[1], mgp = mgp)
2578:                                axis(side = 2, at = axisLabels2$at[!skip], labels = FALSE, mgp = mgp)
2580:                                axis(side = 2, at = axisLabels2$at[!skip], labels = axisLabels2$value[!skip], las = las[2], mgp = mgp)

dankelley avatar Aug 19 '24 14:08 dankelley

I've updated oce so that mapPlot() accepts las as a parameter. This is in the "develop" branch, commit ea6822c2496a2c24840989031be3c7e3c35aa7ad, and a test suite is shown below. Note that it makes 4 images, which I am inserting here in order of creation.

I think las=c(0,0) (the default) and las=c(0,1) will be the most useful cases.

@chrisdane -- if you can build oce from source, please do so and check to see if it seems useful, commenting here on what you find. If you like the results, I'll re-close the issue.

# Demo of why I will set up for las to be a 2-element vector,
# if the user wants more control.
library(oce)
data(coastlineWorld)
png("las_%d.png")
par(mfrow = c(2, 2))
for (las1 in 0:3) {
    for (las2 in 0:3) {
        mapPlot(coastlineCut(coastlineWorld, -100),
            longitudelim = c(-90, -50), latitudelim = c(35, 50),
            las = c(las1, las2),
            projection = "+proj=lcc +lat_0=40 +lat_1=45 +lon_0=-70", col = "gray"
        )
        label <- sprintf(
            "with las = c(%d, %d) %s", las1, las2,
            if (las1 == 0 && las2 == 0) " i.e. the default" else ""
        )
        mtext(label, side = 3, line = 0.5)
    }
}

las_1 las_2 las_3 las_4

dankelley avatar Aug 19 '24 14:08 dankelley

Hi yes that works, thanks a lot.

I am wondering if its possible and, if yes, more generic to pass all default parameters from oce::mapAxis to graphics::axis via ....

Cheers, Chris

chrisdane avatar Aug 19 '24 17:08 chrisdane

@chrisdane that's not really possible because we are talking about things that relate just to axes here.

This is a general thing in R. Imagine some function A in turn calls functions a, b, c, d, etc. If we have a ... parameter for A, then is that to be passed to a, or to b, or to ... or maybe to all of them? Unless all of the lower-case functions handles the same set of items that might be contained within ..., there would be an error of supplying parameters that the functions do not accept.

Not only would this be confusing to the R system, it might be quite confusing to the user, also.

One solution (used by oce and, I think, most R packages) is to name all the things that the user is allowed to control. Another approach is to let A take parameters like maybe aparameters which would be a list of parameters that would get handed down to a(), but not to b(), etc. But, again, that gets confusing for users.

This explains why a lot of R functions (especially those for plotting) have a lot of parameters. And, sometimes, parameters hold parameters within themselves. An example you might know is the control parameter of nls(). This is a list that is normally created by a call to nls.control().

Sorry this is long-winded. The short answer is that using ... is not a solution. Users just have to wade through a long list of parameters for mapPlot() and quite a lot of functions in quite a lot of packages.

Make sense?

dankelley avatar Aug 19 '24 17:08 dankelley

Oh, I forgot to say: I think most users don't use mapAxis(); they just use mapPlot(). In my own work, I often want a lot of control over things, and I sometimes write specialized code to make specialized axes. Doing low-level things like that is not a bad solution, because every new parameter added to a function is one more thing that users might have to think about.

Again, this is quite general, not related to mapPlot() or even to oce.

Generally, and this goes for all my research work, I either go with simple defaults or I make a highly-tailored plot that works for the particular diagram I am making for a particular paper.

dankelley avatar Aug 19 '24 17:08 dankelley

This has been multiple years since the last comment and, since I'm the one who opened it, I am (re)closing it now.

dankelley avatar Dec 09 '24 18:12 dankelley