Been a while since I've blogged here; wrapping up an MSc and moving continents from Europe to North America is all the excuse I need. This blog post is not going to be revolutionary, and obviously it builds on a lot of what others have done before (see towards the end) but I hope enough people find it fun and perhaps informative.
So I've bitten the bullet and emigrated to Edmonton, Alberta (that's the Canadian province just to the east of British Columbia, and to the west of Saskatchewan--north of Idaho, where they grow the potatoes, if you like). I've been pleasantly surprised by the number of individuals I've met with a positive outlook on renewables--this is a bit of a relief, when we landed in Canada and quarantined in Calgary, it felt like I was being bombarded with all sorts of messaging which was pro-oil and gas (read: climate denialist). In fact, there's some data to indicate that the anti-renewables crowd are not representative of Alberta--the "Saudi Arabia of Canada"-- at all. Institutionally, I've also enjoyed reading the output from the Pembina Institute, which seems to specialise in "blue skies optimism" backed up by solid research.
For those who don't know: the cold temperatures and high levels of solar insolation are a perfect mix for solar PV panels. I was particularly drawn to these heat maps drawn up by Pembina's Binnu Jeyakumar: https://www.pembina.org/blog/unlocking-renewables-will-unlock-jobs-alberta At the time I first saw them late last year, I was stuck in finishing my thesis, but now that I've got the time, I'm going to try and replicate one of these maps in R. When finished, I will have drawn a heat map in R which illustrates the differences in solar insolation at "Census Division" level across the Province of Alberta.
When we're done, this is what the map of Alberta is going to look likeLet's start at the beginning. In general, countries have multiple levels of provincial government. For example, any neighborhood in Canada is going to have three layers/levels of government: a Federal government, based in Ottawa, Level 0; a pronvincial government, let's say Alberta, which is Level 1; and a municipal government, with a town council and the like. Let's say we start with Edmonton, which in this case is going to be Level 2. What does a map of Canada look like at that level of granularity? First, download the data from GADM as follows. I also download Level 1 and Level 2 :
library(rgeos) library(maptools) library(ggmap) library(ggplot2) library(sp) library(rgdal) library(raster) #Various packages for GIS and mapping library(plyr) #This one is for database manipulations like "join" library(nasapower) #To get the solar insolation and other meteorological data library(scales) #This one allows us to do things like have nice scales for the legends on the images we create #Download the files for Levels 1 and 2 SPDFs covering Canada can_level1 = getData("GADM", country = "CA", level = 1) can_level2 = getData("GADM", country = "CA", level =2)
plot(can_level1, col = "pink", bg = "lightblue")
> names(can_level1) [1] "GID_0" "NAME_0" "GID_1" "NAME_1" "VARNAME_1" "NL_NAME_1" "TYPE_1" [8] "ENGTYPE_1" "CC_1" "HASC_1"
> can_level1$NAME_1 [1] "Alberta" "British Columbia" "Manitoba" [4] "New Brunswick" "Newfoundland and Labrador" "Northwest Territories" [7] "Nova Scotia" "Nunavut" "Ontario" [10] "Prince Edward Island" "Québec" "Saskatchewan" [13] "Yukon"
> alberta_level1 = can_level1[which(can_level1$NAME_1 == "Alberta"),]
> plot(alberta_level1, col = "green", add = TRUE)
WGS84 <- CRS("+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs") canada_level2_ellipsoid = spTransform(can_level2, WGS84)
alberta_ellipsoid = canada_level2_ellipsoid[which(canada_level2_ellipsoid$NAME_1 == "Alberta"),]
alberta_coordinates = data.frame(matrix(ncol = 2, nrow = length(alberta_ellipsoid$NAME_2))) for(i in 1:length(alberta_ellipsoid$NAME_2)) { alberta_coordinates[i,] = data.frame(gCentroid(alberta_ellipsoid[i,])) }
ghi_retrieved = nasapower::get_power(community = "SSE", pars = "ALLSKY_SFC_SW_DWN", lonlat = c(alberta_coordinates[1,1], alberta_coordinates[1,2]), temporal_average = "CLIMATOLOGY")
Here, "ALLSKY_SFC_SW_DWN" retrieves the GHI or "Global Horizontal Irradiation; and "Climatology" defines the inbuilt timeframe from 1-1-1984 to 31-12-2013. One important thing you'll notice is that the average daily insolation on a GHI basis is stored in column 16 of the returned data.frame (actually a tibble in the first instance from the above line). So we will build an all-Alberta data frame containing the average yearly insolation during the "CLIMATOLOGY" timeframe thus:
alberta_solar_insolation_annual = data.frame(matrix(ncol = 1, nrow = nrow(alberta_coordinates))) for(i in 1:nrow(alberta_coordinates)) { ghi_retrieved = nasapower::get_power(community = "SSE", pars = "ALLSKY_SFC_SW_DWN", lonlat = c(alberta_coordinates[i,1], alberta_coordinates[i,2]), temporal_average = "CLIMATOLOGY") alberta_solar_insolation_annual[i,] = data.frame(ghi_retrieved[16])*365 print("One more Census Division") }
alberta_solar_insolation_annual = cbind(unique(alberta_ellipsoid@data$NAME_2), alberta_solar_insolation_annual) #We set also the metadata colnames(alberta_solar_insolation_annual) = c("NAME_2", "Insolation")
alberta_ellipsoid@data
names(alberta_ellipsoid@data) [1] "GID_0" "NAME_0" "GID_1" "NAME_1" "NL_NAME_1" "GID_2" "NAME_2" "VARNAME_2" [9] "NL_NAME_2" "TYPE_2" "ENGTYPE_2" "CC_2" "HASC_2" "GID_0.1" "NAME_0.1" "GID_1.1" [17] "NAME_1.1" "NL_NAME_1.1" "GID_2.1" "VARNAME_2.1" "NL_NAME_2.1" "TYPE_2.1" "ENGTYPE_2.1" "CC_2.1" [25] "HASC_2.1"
alberta_ellipsoid@data = join(alberta_ellipsoid@data, alberta_solar_df, by = "NAME_2") alberta_solar_df_fortified_B = fortify(alberta_ellipsoid)
ggplot() + geom_polygon(data = alberta_solar_df_fortified_B, aes(x = long, y = lat, group = group, fill = Insolation), size = 0.12) + scale_fill_distiller(name="Insolation, annual \n kWh per m^2", palette = "Spectral", breaks = 7)
This looks pretty and all, but it's also not entirely conclusive of anything if it's not put into perspective. So I repeated the steps above, but instead of Level 2 districts in Alberta alone, I mapped them for the two provinces of Western Canada: Alberta and British Columbia, the latter of which is to the west (left) of the image. As can be clearly seen, Alberta is pretty much consistently more sunny than British Columbia.
Coming up in future posts, I will move on to try and map not only insolation but also more decision variable type values, such as PV yield. A yet more sophisticated approach involves also the intermittency of solar PV and how it matches, or doesn't match, electricity demand (i.e., without storage, PV electricity is no good if people only play video games and turn the lights on at night). That's for much later down the road! To see a similar example which I relied on quite a bit in this case, see here about making a country level map with ggplot2: https://rpubs.com/spoonerf/countrymapggplot2
This comment has been removed by a blog administrator.
ReplyDeleteHello Abed, my name is Ese Omatsone and I am a new student of Solar PV Energy and its proliferation, integration, costing and pricing in Alberta and around the world. I just watched your recorded video presentation made to the CalgaryR user community a few weeks ago and I have been digesting this blog post. I just want to say a big thank you and also ask if I may establish a professional connection with you so I can gain some more knowledge, please. Thank you kindly.
ReplyDeleteHi, thank you. I'm honoured. Yes, please feel free to email me. I think you can find it on the blog?
DeleteGood day, I noticed you used alberta_solar_df in your code but didn't specify what it was for. Will you please provide some clarification?
ReplyDeleteHi. That's the dataframe which contains the annual solar insolation, the coordinates (longitude, latitude) and names of each of the 19 municipalities in Alberta. You then bind that to the separate data frame which exists within the Alberta (stretched) shapefile, the "ellipsoid".
DeleteDoes that answer your question?
Yes it does, thank you and I apologize for the late follow up.
DeleteExcellent. I am aware that the naming conventions for this code snippet ... left a lot to be desired! I need to get my act together about naming objects in my code. Thanks for bringing it to my attention.
Delete