Related
I am trying to label my polygons by using ggplot in R. I found a topic here on stackoverflow that I think is very close to what I want except with points.
Label points in geom_point
I found some methods online. Now I first need to find the central location of each shape and then I have to put these locations together with the name together. Then link this to the labeling function in geom_text()
ggplot centered names on a map
Since I have been trying for a long time now I decided to ask the question and hope that someone here can give me the final push to what I want. My plotting function:
region_of_interest.fort <- fortify(region_of_interest, region = "score")
region_of_interest.fort$id <- as.numeric(region_of_interest.fort$id)
region_of_interest.fort$id <- region_of_interest.fort$id
region_of_interest.fort1 <- fortify(region_of_interest, region = "GM_NAAM")
region_of_interest.fort1$id <- as.character(region_of_interest.fort1$id)
region_of_interest.fort1$id <- region_of_interest.fort1$id
idList <- unique(region_of_interest.fort1$id)
centroids.df <- as.data.frame(coordinates(region_of_interest))
names(centroids.df) <- c("Longitude", "Latitude")
randomMap.df <- data.frame(id = idList, shading = runif(length(idList)), centroids.df)
ggplot(data = region_of_interest.fort, aes(x = long, y = lat, fill = id, group = group)) +
geom_polygon() +
geom_text(centroids.df, aes(label = id, x = Longitude, y = Latitude)) +
scale_fill_gradient(high = "green", low = "red", guide = "colorbar") +
coord_equal() +
theme() +
ggtitle("Title")
It gives me the error: ggplot2 doesn't know how to deal with data of class uneval
My data
region_of_interest$GM_NAAM
[1] Groningen Haren Ooststellingwerf Assen Aa en Hunze Borger- Odoorn
[7] Noordenveld Westerveld Tynaarlo Midden-Drenthe
415 Levels: 's-Gravenhage 's-Hertogenbosch Aa en Hunze Aalburg Aalsmeer Aalten ... Zwolle
region_of_interest$score
[1] 10 -2 -1 2 -1 -4 -4 -5 0 0
Try something like this?
Get a data frame of the centroids of your polygons from the
original map object.
In the data frame you are plotting, ensure there are columns for
the ID you want to label, and the longitude and latitude of those
centroids.
Use geom_text in ggplot to add the labels.
Based on this example I read a world map, extracting the ISO3 IDs to use as my polygon labels, and make a data frame of countries' ID, population, and longitude and latitude of centroids. I then plot the population data on a world map and add labels at the centroids.
library(rgdal) # used to read world map data
library(rgeos) # to fortify without needing gpclib
library(maptools)
library(ggplot2)
library(scales) # for formatting ggplot scales with commas
# Data from http://thematicmapping.org/downloads/world_borders.php.
# Direct link: http://thematicmapping.org/downloads/TM_WORLD_BORDERS_SIMPL-0.3.zip
# Unpack and put the files in a dir 'data'
worldMap <- readOGR(dsn="data", layer="TM_WORLD_BORDERS_SIMPL-0.3")
# Change "data" to your path in the above!
worldMap.fort <- fortify(world.map, region = "ISO3")
# Fortifying a map makes the data frame ggplot uses to draw the map outlines.
# "region" or "id" identifies those polygons, and links them to your data.
# Look at head(worldMap#data) to see other choices for id.
# Your data frame needs a column with matching ids to set as the map_id aesthetic in ggplot.
idList <- worldMap#data$ISO3
# "coordinates" extracts centroids of the polygons, in the order listed at worldMap#data
centroids.df <- as.data.frame(coordinates(worldMap))
names(centroids.df) <- c("Longitude", "Latitude") #more sensible column names
# This shapefile contained population data, let's plot it.
popList <- worldMap#data$POP2005
pop.df <- data.frame(id = idList, population = popList, centroids.df)
ggplot(pop.df, aes(map_id = id)) + #"id" is col in your df, not in the map object
geom_map(aes(fill = population), colour= "grey", map = worldMap.fort) +
expand_limits(x = worldMap.fort$long, y = worldMap.fort$lat) +
scale_fill_gradient(high = "red", low = "white", guide = "colorbar", labels = comma) +
geom_text(aes(label = id, x = Longitude, y = Latitude)) + #add labels at centroids
coord_equal(xlim = c(-90,-30), ylim = c(-60, 20)) + #let's view South America
labs(x = "Longitude", y = "Latitude", title = "World Population") +
theme_bw()
Minor technical note: actually coordinates in the sp package doesn't quite find the centroid, but it should usually give a sensible location for a label. Use gCentroid in the rgeos package if you want to label at the true centroid in more complex situations like non-contiguous shapes.
The accepted answer here may work, but the actual question asked specifically notes that there is an error "ggplot2 doesn't know how to deal with data of class uneval."
The reason that it is giving you the error is because the inclusion of centroids.df needs to be a named variable (e.g. accompanied by "data=")
Currently:
ggplot(data = region_of_interest.fort, aes(x = long, y = lat, fill = id, group = group)) +
geom_polygon() +
geom_text(centroids.df, aes(label = id, x = Longitude, y = Latitude)) +
scale_fill_gradient(high = "green", low = "red", guide = "colorbar") +
coord_equal() +
theme() +
ggtitle("Title")
Should be (note: "data=centroids.df"):
ggplot(data = region_of_interest.fort, aes(x = long, y = lat, fill = id, group = group)) +
geom_polygon() +
geom_text(data=centroids.df, aes(label = id, x = Longitude, y = Latitude)) +
scale_fill_gradient(high = "green", low = "red", guide = "colorbar") +
coord_equal() +
theme() +
ggtitle("Title")
This issue was addressed here: How to deal with "data of class uneval" error from ggplot2?
I am making a map that needs to 2 legends. 1 legend shows the temperature, and another legend shows if points are labelled as zone 1:4. When I try to add the second color scale, for the second legend I get the error
Scale for 'fill' is already present. Adding another scale for
'fill', which will replace the existing scale.
Error: Continuous value supplied to discrete scale
When I don't specify the second color scale I get the map I want however, the points are not the colour I want them to be. I tried using new_scale_color() in the code but that doesn't work either, it's the same error.
Here is some sample data
#Dataframe for Ocean Temperature
mapoc_temp = expand.grid(data.frame(Longitude= seq(-64.5,-62.4,0.1),
Latitude= seq(42.7,44.8,0.1),
year = sample(c(2016,2017,2018), 22, replace = T),
month = sample(month.abb, 22, replace = T)))
mapoc_temp$Ave = runif(nrow(mapoc_temp))
#Dataframe for the points
individual_dets = data.frame(longitude= seq(-64.5,-62.4,0.1),
latitude= seq(42.7,44.8,0.1),
year = sample(c(2016, 2017, 2018), 22, replace = T),
Zone = sample(c(1:4), 22, replace = T),
month = sample(month.abb, 22, replace = T))
This is the code I use to map it, you will need this entire code to see my error.
library(mapdata)
#Import Canada map
canada = map_data("worldHires", "Canada")
## Map of temperature across Scotian Shelf
ggplot(mapoc_temp, aes(x=Longitude, y=Latitude)) +
#plotting the temperature as color
geom_raster(aes(fill = Ave,
x = Longitude), interpolate = TRUE) +
#getting the map of Scotian shelf
geom_polygon(data = canada,
aes(x=long, y=lat, group=group),
colour="grey50", fill = 'grey55')+
#Coordinates I'm interested in looking at
coord_sf(xlim=c(-64.5,-62.8), ylim=c(42.7,45)) +
scale_fill_viridis_c(name = "Temp.\n(°C)", option = "C") +
#Use this to get second legend?
new_scale_color() +
#to try and get zones overtop
geom_point(data = individual_dets,
aes(x = longitude,
y = latitude,
color = as.numeric(Zone),
fill = as.numeric(Zone)),
pch = 21, size = 2) +
#fill the zones with second legend.... Does not work
scale_fill_manual(values=c("#01579B", "#4FC3F7", "#ffa600", "#ff6361")) +
scale_color_manual(values=c("#01579B", "#4FC3F7", "#ffa600", "#ff6361"))
If I don't use the last chunk of code
#fill the zones with second legend.... Does not work
scale_fill_manual(values=c("#01579B", "#4FC3F7", "#ffa600", "#ff6361")) +
scale_color_manual(values=c("#01579B", "#4FC3F7", "#ffa600", "#ff6361"))
I get this picture
But I need the points to be those exact colors I have in the chunk of code. Does anyone know how to add the second legend scale to the ggplot map?
If you don't use a pch = 21, ggplot will use a point with color being the same fill (I guess it is pch = 16 by default), so, you don't need twice scale_fill but instead can manipulate points using scale_color_manual.
Also, you are converting Zone in a numeric format so ggplot2 is plotting it as a continuous scale. So, you can't pass only 4 color values on it. Either, you have to plot Zone as a factor and use scale_color_manual to set the color for each zone (which makes sense) or you can plot Zone as a gradient as you are doing but you need to use scale_color_gradient then to set the color scheme.
library(mapdata)
library(ggplot2)
ggplot(mapoc_temp, aes(x=Longitude, y=Latitude)) +
geom_raster(aes(fill = Ave,
x = Longitude), interpolate = TRUE) +
geom_polygon(data = canada,
aes(x=long, y=lat, group=group),
colour="grey50", fill = 'grey55')+
coord_sf(xlim=c(-64.5,-62.8), ylim=c(42.7,45)) +
scale_fill_viridis_c(name = "Temp.\n(°C)", option = "C") +
geom_point(data = individual_dets,
aes(x = longitude,
y = latitude,
color = as.factor(Zone)),
#fill = as.numeric(Zone)),
size = 2) +
scale_color_manual(values=c("#01579B", "#4FC3F7", "#ffa600", "#ff6361"), name = "Zone")
Does it look what you are trying to achieve ?
I am trying to use ggplot() to plot a betadispers object mod1 so that I can better control the colours.
I extracted the centroids from mod1 and I am using geom_point() for plotting the yearly replicates for each dune , geom_seg()to plot the lines for each dune-star, and a second geom_point() statement to plot the centroids.
When I plot this using
scale_colour_manual(values=cols, guide= FALSE)
it only changes the colour of the first geom_points and the geom_seg but not the centroids.
How do I control the colour of each component separately such that the dune points are coloured by cols, the segments are coloured by cols1 and the centroids use cols2?
I'd also like to change the colour of the black outline for each centroid to cols1.
library(vegan)
library(ggplot2)
cols = c("blue","red")
cols1 = c("green","dark orange")
cols2 = c("purple","yellow")
data(dune)
sites = data.frame(year = rep(c(1:5), times= 4), dune = rep(c(1:4),each=5), dune.type = rep(c("A","B"),each=10))
distances <- vegdist(dune, method = "bray")
#create Betadispersion model on betad (effectively a PCoA)
mod1 <- with(sites, betadisper(distances, dune, type = "centroid"))
s = scores(mod1)
# Get points
pnt_sites = as.data.frame(s$sites)
pnt_sites = cbind(pnt_sites, sites)
# Get centroids
pnt_centroids = as.data.frame(s$centroids)
pnt_centroids$dune = rownames(pnt_centroids)
pnt_centroids$dune.type = rep(c("A","B"),each=2)
# Calculate segments
seg = pnt_sites[, c("PCoA1", "PCoA2", "dune")]
tmp = rename(pnt_centroids, c("PCoA1" = "PCoA1_ctr", "PCoA2" = "PCoA2_ctr"))
seg = join(seg, tmp, "dune")
# Plot
ggplot() +
geom_point(
data = pnt_sites,
aes(x = PCoA1, y = PCoA2, colour = dune.type, shape = dune.type),
size = 2
) +
geom_segment(
data = seg,
aes(x = PCoA1, y = PCoA2, xend = PCoA1_ctr, yend = PCoA2_ctr, colour =
dune.type)
) +
geom_point(
data = pnt_centroids,
aes(x = PCoA1, y = PCoA2, fill = dune.type),
size = 3, shape = 21
) +
scale_colour_manual(values=cols, guide= FALSE) +
coord_equal() +
theme_bw()
You can only specify scale_colour_manual() once per plot, not multiple times for each geom_point call, so you need to combine your centroids and sites into one dataframe (adding variables for centroid/site, and centroid A/centroid B/site A/site B), then plot as a single geom_point() layer
#combine centroids and points into one dataframe
pnt_centroids$year = NA
pnt_centroids$data.type = "centroid"
pnt_sites$data.type = "site"
sites_centroids <- rbind(pnt_centroids, pnt_sites)
sites_centroids$type <- paste(sites_centroids$data.type, sites_centroids$dune.type)
When you define vectors of colors to use for scale_fill_manual and scale_colour_manual, each will have 6 levels, to match the number of variables you have (4 point types plus 2 segment types). Your site points and segments do not have a fill attribute, so fill will be ignored when plotting those points and segments, but you still need to define 6 colors in scale_fill_manual so that your filled points for centroids plot properly.
#change the cols vector definitions at the beginning of code to this
cols.fill <- c("purple", "yellow", "purple", "yellow", "purple", "yellow")
cols.colour <- c("green", "dark orange", "green", "dark orange", "blue", "red")
Specify the new colour, fill, and shape scales in the plot code like this:
# Plot
ggplot() +
geom_segment( #segment must go before point so points are in front of lines
data = seg,
aes(x = PCoA1, y = PCoA2, xend = PCoA1_ctr, yend = PCoA2_ctr, colour = dune.type)) +
geom_point(
data = sites_centroids,
aes(x = PCoA1, y = PCoA2, colour = type, fill = type, shape = type), size = 2) +
scale_colour_manual(values = cols.colour) +
scale_fill_manual(values = cols.fill, guide = FALSE) +
scale_shape_manual(values = c(21, 21, 16, 17)) +
coord_equal() +
theme_bw()
Here is the result. The legend gets a bit busy, it may be better to delete it and use text annotation to label the dune types.
I am trying to label my polygons by using ggplot in R. I found a topic here on stackoverflow that I think is very close to what I want except with points.
Label points in geom_point
I found some methods online. Now I first need to find the central location of each shape and then I have to put these locations together with the name together. Then link this to the labeling function in geom_text()
ggplot centered names on a map
Since I have been trying for a long time now I decided to ask the question and hope that someone here can give me the final push to what I want. My plotting function:
region_of_interest.fort <- fortify(region_of_interest, region = "score")
region_of_interest.fort$id <- as.numeric(region_of_interest.fort$id)
region_of_interest.fort$id <- region_of_interest.fort$id
region_of_interest.fort1 <- fortify(region_of_interest, region = "GM_NAAM")
region_of_interest.fort1$id <- as.character(region_of_interest.fort1$id)
region_of_interest.fort1$id <- region_of_interest.fort1$id
idList <- unique(region_of_interest.fort1$id)
centroids.df <- as.data.frame(coordinates(region_of_interest))
names(centroids.df) <- c("Longitude", "Latitude")
randomMap.df <- data.frame(id = idList, shading = runif(length(idList)), centroids.df)
ggplot(data = region_of_interest.fort, aes(x = long, y = lat, fill = id, group = group)) +
geom_polygon() +
geom_text(centroids.df, aes(label = id, x = Longitude, y = Latitude)) +
scale_fill_gradient(high = "green", low = "red", guide = "colorbar") +
coord_equal() +
theme() +
ggtitle("Title")
It gives me the error: ggplot2 doesn't know how to deal with data of class uneval
My data
region_of_interest$GM_NAAM
[1] Groningen Haren Ooststellingwerf Assen Aa en Hunze Borger- Odoorn
[7] Noordenveld Westerveld Tynaarlo Midden-Drenthe
415 Levels: 's-Gravenhage 's-Hertogenbosch Aa en Hunze Aalburg Aalsmeer Aalten ... Zwolle
region_of_interest$score
[1] 10 -2 -1 2 -1 -4 -4 -5 0 0
Try something like this?
Get a data frame of the centroids of your polygons from the
original map object.
In the data frame you are plotting, ensure there are columns for
the ID you want to label, and the longitude and latitude of those
centroids.
Use geom_text in ggplot to add the labels.
Based on this example I read a world map, extracting the ISO3 IDs to use as my polygon labels, and make a data frame of countries' ID, population, and longitude and latitude of centroids. I then plot the population data on a world map and add labels at the centroids.
library(rgdal) # used to read world map data
library(rgeos) # to fortify without needing gpclib
library(maptools)
library(ggplot2)
library(scales) # for formatting ggplot scales with commas
# Data from http://thematicmapping.org/downloads/world_borders.php.
# Direct link: http://thematicmapping.org/downloads/TM_WORLD_BORDERS_SIMPL-0.3.zip
# Unpack and put the files in a dir 'data'
worldMap <- readOGR(dsn="data", layer="TM_WORLD_BORDERS_SIMPL-0.3")
# Change "data" to your path in the above!
worldMap.fort <- fortify(world.map, region = "ISO3")
# Fortifying a map makes the data frame ggplot uses to draw the map outlines.
# "region" or "id" identifies those polygons, and links them to your data.
# Look at head(worldMap#data) to see other choices for id.
# Your data frame needs a column with matching ids to set as the map_id aesthetic in ggplot.
idList <- worldMap#data$ISO3
# "coordinates" extracts centroids of the polygons, in the order listed at worldMap#data
centroids.df <- as.data.frame(coordinates(worldMap))
names(centroids.df) <- c("Longitude", "Latitude") #more sensible column names
# This shapefile contained population data, let's plot it.
popList <- worldMap#data$POP2005
pop.df <- data.frame(id = idList, population = popList, centroids.df)
ggplot(pop.df, aes(map_id = id)) + #"id" is col in your df, not in the map object
geom_map(aes(fill = population), colour= "grey", map = worldMap.fort) +
expand_limits(x = worldMap.fort$long, y = worldMap.fort$lat) +
scale_fill_gradient(high = "red", low = "white", guide = "colorbar", labels = comma) +
geom_text(aes(label = id, x = Longitude, y = Latitude)) + #add labels at centroids
coord_equal(xlim = c(-90,-30), ylim = c(-60, 20)) + #let's view South America
labs(x = "Longitude", y = "Latitude", title = "World Population") +
theme_bw()
Minor technical note: actually coordinates in the sp package doesn't quite find the centroid, but it should usually give a sensible location for a label. Use gCentroid in the rgeos package if you want to label at the true centroid in more complex situations like non-contiguous shapes.
The accepted answer here may work, but the actual question asked specifically notes that there is an error "ggplot2 doesn't know how to deal with data of class uneval."
The reason that it is giving you the error is because the inclusion of centroids.df needs to be a named variable (e.g. accompanied by "data=")
Currently:
ggplot(data = region_of_interest.fort, aes(x = long, y = lat, fill = id, group = group)) +
geom_polygon() +
geom_text(centroids.df, aes(label = id, x = Longitude, y = Latitude)) +
scale_fill_gradient(high = "green", low = "red", guide = "colorbar") +
coord_equal() +
theme() +
ggtitle("Title")
Should be (note: "data=centroids.df"):
ggplot(data = region_of_interest.fort, aes(x = long, y = lat, fill = id, group = group)) +
geom_polygon() +
geom_text(data=centroids.df, aes(label = id, x = Longitude, y = Latitude)) +
scale_fill_gradient(high = "green", low = "red", guide = "colorbar") +
coord_equal() +
theme() +
ggtitle("Title")
This issue was addressed here: How to deal with "data of class uneval" error from ggplot2?
I am trying to add state abbreviations to a US map generated using ggplot2 and having some difficulties with it. I believe the "fill = " option is causing it, but I am not sure.
Below I provide the code I am using. Initially, I generate the map the way I want except for the state names. Next, I try to overlay state abbreviations on the same map.
Unfortunately it is not working out for me. If I comment out "fill = " option from the first map, I can generate a map with state abbreviations. But that map does not show what I intend to show. I have tried several ways. I am just leaving one option in the code for the moment.
To add the state abbreviations, I am following some of the suggestions I have read in this forum. In particular, I am trying to follow the advice from a discussion titled "ggplot centered names on a map" dated February 25, 2012.
I would appreciate any help on how I can add/overlay the state abbreviations to the first map.
# Master US location data
states <- map_data("state")
# Read in the data
rate <- read.csv("~/R/HealthCare/Data/Test_data.csv")
names(rate) <- tolower(names(rate))
rate$numer <- as.factor(rate$numer)
rate$region <- tolower(rate$statename)
# Create data for US mapping
tomap <- merge(states, rate, sort = FALSE, by = "region")
tomap <- tomap[order(tomap$order), ]
## US Map
# 1. Target Map (w/o state abbr)
p <- qplot(long, lat, data = tomap,
group = group,
fill = numer,
geom = "polygon")
p + scale_fill_brewer(palette = "Greens",
guide = guide_legend(reverse = TRUE),
labels = c("1st cat", "2nd cat",
"3rd cat", "4th cat"))
# 2. Add State Abbreviations to Target Map
stannote <- aggregate(cbind(long, lat, group, numer) ~ stateabbr, data = tomap,
FUN=function(x)mean(range(x)))
q <- qplot(long, lat, data = tomap,
group = group,
#fill = numer,
fill = "red", #testing
geom = "polygon") +
geom_text(data=stannote, aes(long, lat, label = stateabbr), color = "blue", size=3) +
coord_map()
q
The sample data file looks like the following –
StateName,StateAbbr,Numer
Alabama,AL,0
Alaska,AK,0
Arizona,AZ,0
Arkansas,AR,0
California,CA,0
Colorado,CO,0
Connecticut,CT,0
Delaware,DE,0
District of Columbia,DC,1
Florida,FL,0
Georgia,GA,0
Hawaii,HI,0
Idaho,ID,1
Illinois,IL,0
Indiana,IN,0
Iowa,IA,1
Kansas,KS,0
Kentucky,KY,1
Louisiana,LA,1
Maine,ME,2
Maryland,MD,0
Massachusetts,MA,2
Michigan,MI,0
Minnesota,MN,1
Mississippi,MS,0
Missouri,MO,0
Montana,MT,0
Nebraska,NE,0
Nevada,NV,1
New Hampshire,NH,1
New Jersey,NJ,2
New Mexico,NM,1
New York,NY,3
North Carolina,NC,0
North Dakota,ND,1
Ohio,OH,0
Oklahoma,OK,0
Oregon,OR,2
Pennsylvania,PA,0
Rhode Island,RI,0
South Carolina,SC,0
South Dakota,SD,1
Tennessee,TN,0
Texas,TX,0
Utah,UT,1
Vermont,VT,2
Virginia,VA,0
Washington,WA,2
West Virginia,WV,0
Wisconsin,WI,0
Wyoming,WY,0
As often happens to me with R, it turns out the error message was telling you exactly what was happening (it just takes a while to figure it out). Your numer variable in your second dataset stannote is continuous (check the structure with str(stannote) to see this). So you can just change that variable to a factor. Watch out, though: when you used cbind in aggregate I think you forced the factor to be turned into a numeric variable and so numer in stannote goes from 1-4 instead of 0-3.
Option 1:
stannote$numer = factor(stannote$numer, labels = c(0, 1, 2, 3))
qplot(long, lat, data = tomap,
group = group,
fill = numer, #testing
geom = "polygon") +
geom_text(data=stannote, aes(long, lat, label = stateabbr),
color = "blue", size=3) + scale_fill_brewer(palette = "Greens")
Alternatively, you could remove the fill aesthetic that you set for the overall plot from the call to geom_text using fill = NULL. You don't actually need fill for the text, just for the polygons. This is a situation where if you were using ggplot instead of qplot you might just set the fill aesthetic for geom_polygon.
Option 2:
stannote$numer = as.numeric(stannote$numer)
qplot(long, lat, data = tomap,
group = group,
fill = numer, #testing
geom = "polygon") +
geom_text(data=stannote, aes(long, lat, label = stateabbr, fill = NULL),
color = "blue", size=3) + scale_fill_brewer(palette = "Greens")