hrbrmstr / ggalt

:earth_americas: Extra Coordinate Systems, Geoms, Statistical Transformations & Scales for 'ggplot2'
https://cran.r-project.org/web/packages/ggalt/vignettes/ggalt_examples.html
Other
663 stars 98 forks source link

Some projections don't show polygons spaning all longitudes #49

Open eliocamp opened 6 years ago

eliocamp commented 6 years ago

Don't know if this is an issue of ggalt or a more fundamental problem with coordinate transformations, but using coord_proj(), some projections make large polygons disappear.

library(ggplot2)
world <- map_data("world")
world <- subset(world, lat < 0)
geom_world <- geom_path(aes(long, lat, group = group), data = world, 
                        color = "gray50", size = 0.2)

This square that covers the whole domain works well with no coord

square <- data.frame(lon = c(-180, -180, 180, 180), 
                     lat = c(-90, 0, 0, -90))

(ggplot(square, aes(lon, lat)) +
      geom_world +
   geom_polygon() -> g)

or with coord_polar()

g + coord_polar()

But in coord_proj() it doesn't show up.

g + ggalt::coord_proj("+proj=stere +lat_0=-90")

This also affects ggplot2's coord_map(), so it might be a more fundamental problem? Changing the limits works, but it appears as a low resolution polygon.

square <- data.frame(lon = c(-180, -180, 170, 170), 
                        lat = c(-80, -10, -10, -80))

(g <- g %+% square + ggalt::coord_proj("+proj=stere +lat_0=-90"))

which is actually lower resolution if the limits are increased.

While this seems like a nitche problem, it's actually a very big issue when plotting filled contours because (at least in my implementation) there's always a square that acts as "background" so that every part of the map is filled. Here's an example:

library(metR)  # for dataset and geom_contour_fill()
temperature$lon <- with(temperature, ConvertLongitude(lon))
ggplot(subset(temperature, lev == 200 & lat < 0), aes(lon, lat)) +
   geom_contour_fill(aes(z = air), circular = "x") +
   ggalt::coord_proj("+proj=stere +lat_0=-90")

For refference, here's how it should look :)

ggplot(subset(temperature, lev == 200 & lat < 0), aes(lon, lat)) +
   geom_contour_fill(aes(z = air), circular = "x") +
   coord_polar()

hrbrmstr commented 6 years ago

This has been a longstanding "issue" with the built-in map data. Can you give it a shot with the rnaturalearth package data (I have another world geojson on my personal system — on work system right now — that I can also upload to test with too)

eliocamp commented 6 years ago

I don't think is a problem with the map data. I put it there mostly for reference, but I get the same problem with only the square:

library(ggplot2)
square <- data.frame(lon = c(-180, -180, 180, 180), 
                     lat = c(-90, 0, 0, -90))
ggplot(square, aes(lon, lat)) +
      geom_polygon() + 
      ggalt::coord_proj("+proj=stere +lat_0=-90")

Using rnaturalearth world coastlines also returns the same.

mdsumner commented 6 years ago

Looks like there's no coord_munching as when coord_polar is used?

square_munched <- as.data.frame(lapply(square, appf))

(ggplot(square_munched, aes(lon, lat)) +
    geom_world +
    geom_polygon() -> g)

g + ggalt::coord_proj("+proj=stere +lat_0=-90")

It's complicated to apply that sensibly to the temperature data. I don't know of a general way to handle this in R, ultimately you need curvature-aware densification as in TopoJSON apps, there's nothing similar in R afaik, though a controllable coord_munch might be enough to work.

eliocamp commented 6 years ago

I'm sorry, what's appf?

eliocamp commented 6 years ago

Thanks @mdsumner, you'r comment got me on the right track to find a workaround on my end.

library(metR)  # for dataset and geom_contour_fill()
library(ggplot2)
data(temperature)

ggplot(subset(temperature, lev == 200 & lat < 0), aes(lon, lat)) +
    geom_contour_fill(aes(z = air), xrange = c(-180, 180)) +
    ggalt::coord_proj("+proj=stere +lat_0=-90")

mdsumner commented 6 years ago

Oh cool, appf is a wrapper on approxfun something like function (x) approxfun(seq_along(x),x)(seq(1, length(x), length.out = 300) - sorry about that!