I´m having problems when combining a map from Google Map Api, with a map constructed with geom_polygon from ggplot2. When I plot each map on their own, nothing weird shows up, but when I do combine them, some lines(straight) appear, messing up the boundaries I intend to highlight.
1) The data I´m using to construct the polygos with the black borders comes from this link. The exact url of the file is in my code below. Sometimes it is necessary to unzip the file manually, therefore the code below for downloading the data may not work:
path <- getwd()
fileName <- "R05.zip"
if (!file.exists(fileName)) {
urlFile = "http://www.censo2017.cl/wp-content/uploads/2016/12/R05.zip"
download(urlFile, dest = "./R05.zip", mode ="wb")
}
if (!dir.exists("./R05")) {
unzip("R05.zip")
}
2) Then I load the shapefile with which I´ll construct the polygons.
distritos <- readOGR( dsn= paste(getwd(), "/R05", sep = ""),
layer="Distritos_Censales",
encoding = "UTF-8", stringsAsFactors = FALSE)
3) And select the administrative division I´m interested in
distritos <- distritos[distritos#data$DESC_COMUN=="QUILPUÉ", ]
4) And then the districts (polygons) I´m interested in:
distritos <- distritos[distritos#data$DESC_DISTR=="EL RETIRO" |
distritos#data$DESC_DISTR=="BELLOTO NORTE" |
distritos#data$DESC_DISTR=="VALENCIA" |
distritos#data$DESC_DISTR=="MENA" |
distritos#data$DESC_DISTR=="BELLOTO SUR" |
distritos#data$DESC_DISTR=="ALTO QUILPUÉ" |
distritos#data$DESC_DISTR=="EL SAUCE", ]
5) Construct the base map from the boundaries of distritos. For that,
I use the function to get the center of a map from this StackOverflow question
bbox(distritos)
MapCenter <- function(x1, y1, x2, y2){
center.x <- x1 + ((x2 - x1) / 2)
center.y <- y1 + ((y2 - y1) / 2)
center <- c(center.x, center.y)
center
}
mcdistritos <- MapCenter(bbox(distritos)[1,1], bbox(distritos)[2,1],
bbox(distritos)[1,2], bbox(distritos)[2,2])
basemap <- get_googlemap(mcdistritos, zoom = 13,
maptype = "roadmap",
color = "bw",
style = "feature:administrative|element:labels|visibility:off")
basemap <- ggmap(basemap , extent = "device")
6) prepare the shapefile data to plot it with ggplot2
distritos.fort <- fortify(distritos, region = "DESC_DISTR")
7) Plot together both maps
basemap +
geom_polygon(data = distritos.fort,
aes(x = long, y = lat),
colour = "black",
fill = NA) + coord_map()
I tried by zooming out the base map, in case the polygon boundaries were messed up cause they didn´t fit in the base map, but I got the same result, just a smaller map. Does anyone know how to fix it?
You need to add a group mapping to your aesthetic. e.g.
geom_polygon(data = distritos.fort,
aes(x = long, y = lat, group = group),
colour = "black",
fill = NA) + coord_map()
At the moment you've got one continuous path. The group aesthetic separates your data into different polygons.
I wasn't able to extract your data, so I don't know the exact mapping you require for group. But looking at the other fortify examples & documentation, I believe it is group = group
Related
I have a dataframe object, created by reading in a shape file with sf::read_sf and merged with some pre-existing data with a common geography column:
boundaries <- sf::read_sf('./shapefile')
map <- merge(boundaries, data, by.x = "InterZone",
by.y = "IntermediateZone2011Code", all.x = FALSE, duplicateGeoms = TRUE)
This is then overlaid using ggmap on top of a provider tile obtained with the sf get_map function:
myMap <- get_map(location = c(lon = -2.27, lat = 57.1), zoom = 6,
maptype="toner", crop=FALSE, source = 'stamen')
ggmap(myMap) +
geom_sf(data = map, aes(fill=as.factor(column1)), inherit.aes = FALSE) +
scale_fill_brewer(palette = "OrRd") +
coord_sf(crs = st_crs(4326)) +
labs(x = 'Longitude', y = 'Latitude', fill = 'column1') +
ggtitle('column1')
The issue is that this auto creates hundreds of bins.
I have been looking through the documentation but cannot find an additional argument to specify the number of bins. How can I make it clear to breakdown the column by a fixed number of bins and then map this?
Without a reproducible example it is hard to say exactly what is going on, but it looks like you might be converting a continuous variable into a factor with fill=as.factor(column1).
One option is you remove as.factor and use scale_fill_continuous or some other continuous color scale of your choice.
Another option is to look into cut, where you bin continuous data by specifying the number of bins, or the specific start and end points of your bins.
# Make n bins
map$data_bin <- cut(map$column, breaks = n )
# Or make specific start and end points for bins
map$data_bin <- cut(map$column, breaks = c(-Inf,50,100,Inf) )
I would like to reproduce plot of spatial dependency of regions in ggplot2 rather then using basic plot in R
I provided reproduceble example in code below:
I followed example: Plotting neighborhoods network to a ggplot maps
library(leaflet)
library(ggplot2)
library(sf)
library(spdep)
URL <- "https://biogeo.ucdavis.edu/data/gadm3.6/Rsp/gadm36_CZE_1_sp.rds"
data <- readRDS(url(URL))
ggplot() +
geom_polygon(data = data, aes(x=long, y = lat, group = group), color = "black", fill = F)
cns <- knearneigh(coordinates(data), k = 3, longlat=T)
scnsn <- knn2nb(cns, row.names = NULL, sym = T)
cns
scnsn
cS <- nb2listw(scnsn)
summary(cS)
# Plot of regions and k-nn neighthorhours matrix
plot(data)
plot(cS, coordinates(data), add = T)
I am asking how to reproduce Plot of regions and k-nn neighthorhours matrix using ggplot.
I know we have to retrive each point input and then use geom_segment, however I dont know how to retrive it from cS object.
The other SO post you are refering contains all steps you need to follow to get your plot (thanks to the great answer from #StupidWolf).
Basically, you need to extract the different segment using:
1) Transform coordinates of data in a dataframe, it will facilitate its use later:
data_df <- data.frame(coordinates(data))
colnames(data_df) <- c("long", "lat")
This data_df contains now all x,y values for plotting points.
2) Now, we can retrieve segments informations from the cS object using:
n = length(attributes(cS$neighbours)$region.id)
DA = data.frame(
from = rep(1:n,sapply(cS$neighbours,length)),
to = unlist(cS$neighbours),
weight = unlist(cS$weights)
)
DA = cbind(DA, data_df[DA$from,], data_df[DA$to,])
colnames(DA)[4:7] = c("long","lat","long_to","lat_to")
In the DA dataframe, you have all informations required to draw each segments
3) Finally, you can put plot every parts:
ggplot(data, aes(x = long, y =lat))+
geom_polygon(aes(group = group), color = "black", fill = FALSE)+
geom_point(data = data_df, aes(x= long, y = lat), size = 1)+
geom_segment(data = DA, aes(xend = long_to, yend = lat_to), size=0.5)
Again, the solution provided by #StupidWolf was pretty well written and understandable, so I don't know why you were not able to reproduce it.
I made static heatmaps with the library(ggmap) and the stat_density2d() function. Looking to recreate this in a shiny app on a dynamic leaflet map, I found addHeatmap(). However, the resulting images are dissimilar, with the ggmap version seemingly offering the correct result.
GGMAP
LEAFLET
What is causing this difference?
To run both of the below reproducible examples, you can download some data (csv file) I put here.
https://drive.google.com/drive/folders/0B8_GTHBuoKSRR1VIRmhOUTJKYU0?usp=sharing
Note that the leaflet result differs with zoom level, but never matches the ggmap result (e.g. in terms location of maximum heat).
This is the ggmap code.
library(ggmap)
data <- read.csv("DATA.csv", sep=";")
data <- subset(data, !is.na(CrdLatDeg))
xmin <- min(data$CrdLonDeg)
xmax <- max(data$CrdLonDeg)
ymin <- min(data$CrdLatDeg)
ymax <- max(data$CrdLatDeg)
lon <- c(xmin,xmax)
lat <- c(ymin,ymax)
map <- get_map(location = c(lon = mean(lon), lat = mean(lat)), zoom = 17,
maptype = "satellite", source = "google")
ggmap(map) +
labs(x="longitude", y="latitude") +
stat_density2d(data=data, aes(x=CrdLonDeg, y=CrdLatDeg, alpha= ..level.., fill= ..level..), colour=FALSE,
geom="polygon", bins=100) +
scale_fill_gradientn(colours=c(rev(rainbow(100, start=0, end=.7)))) + scale_alpha(range=c(0,.8)) +
guides(alpha=FALSE,fill=FALSE)
This is the leaflet code.
library(leaflet.extras)
data <- read.csv("DATA.csv", sep=";")
data <- subset(data, !is.na(CrdLatDeg))
leaflet(data) %>%
addTiles(group="OSM") %>%
addHeatmap(group="heat", lng=~CrdLonDeg, lat=~CrdLatDeg, max=.6, blur = 60)
The images look different because the algorithms are different.
stat_density2d() extrapolates a probability density function from the discrete data.
Leaflet implementation of heatmaps rely on libraries like simpleheat, heatmap.js or webgl-heatmap. These heatmaps do not rely on probability density. (I'm not fully sure which of these is used by r-leaflet's addHeatmap).
Instead, these heatmaps work by drawing a blurred circle for each point, raising the value of each pixel by an amount directly proportional to the intensity of the point (constant in your case), and inversely proportional to the distance between the point and the circle. Every data point is shown in the heatmap as a circle. You can see this by playing with your mouse cursor in the heatmap.js webpage, or by looking at this lone point in the top-right of your image:
Think of a heatmap like a visualization of the function
f(pixel) = ∑ ( max( 0, radius - distance(pixel, point) ) · intensity(point) )
One can tweak the radius and intensity of heatmaps, but the result will never be the same as a statistical density estimation.
I've found this answer over at GIS, and I've attempted to create a function and applied it to this case. I haven't figured out how to finetune the colour gradient scheme as of yet, but it seems like a good first start otherwise:
library(leaflet)
library(rlang)
addHeatMap <- function(data, lon, lat, intensity, show.legend, ...) {
df <- data.table::as.data.table(data)
df_expanded <- dplyr::slice(df, rep(1:dplyr::n(), times = !! enquo(intensity)))
lon_var <- dplyr::pull(df_expanded, !! enquo(lon))
lat_var <- dplyr::pull(df_expanded, !! enquo(lat))
lon_bw <- MASS::bandwidth.nrd(lon_var)
lat_bw <- MASS::bandwidth.nrd(lat_var)
lon_lat_df <- dplyr::select(df_expanded, !! enquo(lon), !! enquo(lat))
kde <- KernSmooth::bkde2D(lon_lat_df, bandwidth = c(lon_bw, lat_bw))
CL <- contourLines(kde$x1 , kde$x2 , kde$fhat)
LEVS <- as.factor(sapply(CL, `[[`, "level"))
NLEV <- nlevels(LEVS)
pgons <- lapply(1:length(CL), function(i)
sp::Polygons(list(sp::Polygon(cbind(CL[[i]]$x, CL[[i]]$y))), ID = i))
spgons <- sp::SpatialPolygons(pgons)
if (show.legend) {
leaflet::addPolygons(data = spgons, color = heat.colors(NLEV, NULL)[LEVS], stroke = FALSE, ...) %>%
leaflet::addLegend(colors = heat.colors(NLEV, NULL)[LEVS], labels = LEVS)
} else {
leaflet::addPolygons(data = spgons, color = heat.colors(NLEV, NULL)[LEVS], stroke = FALSE, ...)
}
}
mydata <- read.csv("DATA.csv", sep=";")
mydata <- subset(mydata, !is.na(CrdLatDeg))
leaflet() %>%
addTiles(group = "OSM") %>%
addHeatMap(data = mydata, lon = CrdLonDeg, lat = CrdLatDeg, intensity = FsmIdf, show.legend = TRUE)
Both use a different algorithm. You need to tweak the radius and blur arguments of addHeatmap and the h argument of stat_density2d to get somewhat similar results.
I want to plot a map with a raster overlaying a GoogleMaps base map in ggplot2. Therefore, I used get_map() and insert_raster() like this:
library(ggplot2)
library(ggmap)
bm <- ggmap(get_map(location = "Bangkok", maptype = "hybrid"))
bm + inset_raster(as.raster(r), xmin = r#extent[1], xmax = r#extent[2],
ymin = r#extent[3], ymax = r#extent[4])
Is there any possibility to set a alpha and change the fill color?
The result looks like this:
Even Faster without fortify:
read the original post below for further information
From this blog entry I found that we can use spatial polygons directly in ggplot::geom_polygon()
r <- raster(system.file("external/test.grd", package="raster"))
# just to make it reproducible with ggmap we have to transform to wgs84
r <- projectRaster(r, crs = CRS("+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs"))
rtp <- rasterToPolygons(r)
bm <- ggmap(get_map(location = bbox(rtp), maptype = "hybrid", zoom = 13))
bm +
geom_polygon(data = rtp,
aes(x = long, y = lat, group = group,
fill = rep(rtp$test, each = 5)),
size = 0,
alpha = 0.5) +
scale_fill_gradientn("RasterValues", colors = topo.colors(255))
How to tackle plotting SPEED if you just need to visualize something
As described below, such plotting might become very slow with large numbers of pixels. Therefore, you might consider to reduce the number of pixels (which in most cases does not really decrease the amount of information in the map) before converting it to polygons. Therefore, raster::aggregate can be used to reduce the number of pixels to a reasonable amount.
The example shows how the number of pixels is decreased by an order of 4 (i.e. 2 * 2, horizontally * vertically). For further information see ?raster::aggregate.
r <- aggregate(r, fact = 2)
# afterwards continue with rasterToPolygons(r)...
Original Post:
After a while, I found a way to solve this problem. Converting the raster to polygons! This idea then basically was implemented after Marc Needham's blog post.
Yet, there is one drawback: ggplot gets really slow with large numbers of polygons, which you will inevitably face. However, you can speed things up by plotting into a png() (or other) device.
Here is a code example:
library(raster)
library(ggplot2)
library(ggmap)
r <- raster(....) # any raster you want to plot
rtp <- rasterToPolygons(r)
rtp#data$id <- 1:nrow(rtp#data) # add id column for join
rtpFort <- fortify(rtp, data = rtp#data)
rtpFortMer <- merge(rtpFort, rtp#data, by.x = 'id', by.y = 'id') # join data
bm <- ggmap(get_map(location = "Shanghai", maptype = "hybrid", zoom = 10))
bm + geom_polygon(data = rtpFortMer,
aes(x = long, y = lat, group = group, fill = layer),
alpha = 0.5,
size = 0) + ## size = 0 to remove the polygon outlines
scale_fill_gradientn(colours = topo.colors(255))
This results in something like this:
just been looking into this myself. The issue i encountered was trying to overlay a ggmap output with a raster was the following error:
Error: geom_raster only works with Cartesian coordinates.
the work around to this issue is to use coord_cartesian() as follows:
library(ggplot2)
library(ggmap)
bm <- ggmap(get_map(location = "Bangkok", maptype = "hybrid"))
bm <- bm + geom_raster(...) # insert your raster here
bm <- bm + coord_cartesian()
plot(bm)
I am not sure where your raster r is coming from. for this to work simply convert your raster r into a data frame and add the data according to the geom_raster() instructions, ensure the coordinates are in lat/long (i.e. same as the map).
To answer your question, through geom_raster() you can manipulate alpha and fill.
Hope this helps.
btw this work around was originally raised at this link:
https://groups.google.com/forum/embed/#!topic/ggplot2/nqzBX22MeAQ
I am attempting to make a map with three layers using ggmap. The layers are as follows:
A map of the US (toner-lite)
a set of geometries that color the states on some value (simulated data below)
labels for the state names, as annotations in the center of each state.
To do this I have created a map of US states with states colored by a randomized value (rnorm) and this part is successful. From here I am attempting to print the abbreviations of each state at the longitude and latitude coordinates of each state's center, using geom_text. The part that fails is the 'geom_text' overlay, with the following error:
Error: 'x' and 'units' must have length > 0 In addition: Warning
messages: 1: In gpclibPermit() : support for gpclib will be
withdrawn from maptools at the next major release 2: Removed 855070
rows containing missing values (geom_text).
Here is the script, which I have worked hard to run as on its own. It will download the shapefile and center of state data, as well as to simulate data to fill the states. I've tested it and it works up to what I have commented out (geom_text layer).
I have searched for answers to this already, so please let me know if you have any advice on how to do what I am attempting. If there is a better strategy for placing labels on top of the polygon fills, I am all ears (or eyes in this case).
###Combining Census data with a tract poly shapefile
library(maptools)
library(ggplot2)
library(gpclib)
library(ggmap)
library(rgdal)
library(dplyr)
#Set working directory to where you want your files to exist (or where they already exist)
setwd('~/Documents/GIS/USCensus/')
#Read and translate coord data for shape file of US States
if(!file.exists('tl_2014_us_state.shp')){
download.file('ftp://ftp2.census.gov/geo/tiger/TIGER2014/STATE/tl_2014_us_state.zip',
'tl_2014_us_state.zip')
files <- unzip('tl_2014_us_state.zip')
tract <- readOGR(".","tl_2014_us_state") %>% spTransform(CRS("+proj=longlat +datum=WGS84"))
} else {
tract <- readOGR(".","tl_2014_us_state") %>% spTransform(CRS("+proj=longlat +datum=WGS84"))
}
#two column dataset of state abbreviations and center of state
#Downloadable from: https://dev.maxmind.com/static/csv/codes/state_latlon.csv
if(!file.exists('state_latlon.csv')){
download.file('http://dev.maxmind.com/static/csv/codes/state_latlon.csv','state_latlon.csv')
}
centers <- read.csv('state_latlon.csv')
#Change values of longitude and latitude from state center data so as not to interfere with shapefile at merge
names(centers)[2:3] <- c('long_c','lat_c')
#simulated data for plotting values
mydata<- data.frame(rnorm(55, 0, 1)) #55 "states" in the coord dataset for state centers
names(mydata)[1] <- 'value'
#hold names in tract dataset and for simulated data
ntract<-names(tract)
ndata<-names(mydata)
#Turn geo data into R dataframe
gpclibPermit()
tract_geom<-fortify(tract,region="STUSPS")
#Merge state geo data with simulated data
state_data <- cbind(centers,mydata)
#merge state center and value data with shapefile data
tract_poly <- merge(state_data,tract_geom,by.x="state",by.y="id", all = F)
tract_poly<-tract_poly[order(tract_poly$order),]
#Create map of US
mymap <- get_stamenmap(bbox = c(left = -124.848974,
bottom = 24.396308,
right = -66.885444,
top = 49.384358),zoom=5,
maptype="toner-lite")
#This plots a map of the US with just the state names as labels (and a few other landmarks). Used for reference
USMap <- ggmap(mymap,extent='device') +
geom_polygon(aes(x = long, y = lat, group = group, fill = value),
data = tract_poly,
alpha = 1,
color = "black",
size = 0.2) #+
# geom_text(aes(x = long_c, y = lat_c, group = group, label = state),
# data= tract_poly,
# alpha = 1,
# color = "black")
USMap
That's a strange error message for what ended up being the problem. Somewhere along the way you have flipped the latitude and longitude for centers. (I also took into account elpi's advice above and didn't plot the Initials repeatedly by using your centers dataset directly). The code below works, but I'd recommend fixing your centers dataset.
centers$new_long <- centers$lat_c
centers$new_lat <- centers$long_c
USMap <- ggmap(mymap,extent='device') +
geom_polygon(aes(x = long, y = lat, group = group, fill = value),
data = tract_poly,
alpha = 1,
color = "black",
size = 0.2) +
geom_text(aes(x = new_long, y = new_lat, label = state),
data= centers,
alpha = 1,
color = "black")
Try this
centroids <- setNames(do.call("rbind.data.frame", by(tract_poly, tract_poly$group, function(x) {Polygon(x[c('long', 'lat')])#labpt})), c('long', 'lat'))
centroids$label <- tract_poly$state[match(rownames(centroids), tract_poly$group)]
USMap + with(centroids, annotate(geom="text", x = long, y=lat, label = label, size = 2.5))
(via)