While I realize that putting legends on maps using the Rstudio leaflet package is still a work in progress, I've been trying to add a legend post-hoc to the HTML that R generates.
library("leaflet")
set.seed(100)
pdf <- data.frame(Latitude = runif(100, -90,90), Longitude = runif(100, -180,180),
col=rep(c("red", "blue"), 50 ))
#just red
leaflet(pdf) %>% addTiles() %>%
addCircleMarkers(lat = ~ Latitude, lng = ~ Longitude, color= ~col)
I've been trying to adapt the code from http://leafletjs.com/examples/choropleth.html and figure out where to add it to the output from running the above code in R and turning it into HTML.
So something like putting the following in the body of the html:
<script>
var legend = L.control({position: 'bottomright'});
legend.onAdd = function (map) {
var div = L.DomUtil.create('div', 'info legend'),
grades = [red, blue],
labels = [];
// loop through our density intervals and generate a label with a colored square for each label
for (var i = 0; i < grades.length; i++) {
div.innerHTML +=
'<i style="background:' + getColor(grades[i] + 1) + '"></i> ' +
grades[i] + (grades[i + 1] ? '–' + grades[i + 1] + '<br>' : '+');
}
return div;
};
legend.addTo(map);
<script>
This doesn't seem to work, however. Nothing pops up. Nor is it clear how I would use names other than 'red', and 'blue' for the grades, as it were. I've also added in CSS as shown in the choropleth example as well, but no dice.
Has anyone done this - manually added a legend to their R output (say, grabbing the source from Rpubs after publishing) to add a legend?
While this is not quite what I was looking for, it appears that an addLegend function is on its way, and is in one branch of the package. See some documentation and and an example here:
http://smartinsightsfromdata.github.io//2015/04/25/choropleths.html
Related
I'm trying to draw car trips on a plane. I'm using Plotters library.
Here is some code example of trips' drawing procedure:
pub fn save_trips_as_a_pic<'a>(trips: &CarTrips, resolution: (u32, u32))
{
// Some initializing stuff
/// <...>
let root_area =
BitMapBackend::new("result.png", (resolution.0, resolution.1)).into_drawing_area();
root_area.fill(&WHITE).unwrap();
let root_area =
root_area.margin(10,10,10,10).titled("TITLE",
("sans-serif", 20).into_font()).unwrap();
let drawing_areas =
root_area.split_evenly((cells.1 as usize, cells.0 as usize));
for (index, trip) in trips.get_trips().iter().enumerate(){
let mut chart =
ChartBuilder::on(drawing_areas.get(index).unwrap())
.margin(5)
.set_all_label_area_size(50)
.build_ranged(50.0f32..54.0f32, 50.0f32..54.0f32).unwrap();
chart.configure_mesh().x_labels(20).y_labels(10)
.disable_mesh()
.x_label_formatter(&|v| format!("{:.1}", v))
.y_label_formatter(&|v| format!("{:.1}", v))
.draw().unwrap();
let coors = trip.get_points();
{
let draw_result =
chart.draw_series(series_from_coors(&coors, &BLACK)).unwrap();
draw_result.label(format!("TRIP {}",index + 1)).legend(
move |(x, y)|
PathElement::new(vec![(x, y), (x + 20, y)], &random_color));
}
{
// Here I put red dots to see exact nodes
chart.draw_series(points_series_from_trip(&coors, &RED));
}
chart.configure_series_labels().border_style(&BLACK).draw().unwrap();
}
}
What I got now on Rust Plotters:
So, after drawing it in the 'result.png' image file, I struggle to understand these "lines", because I don't see the map itself. I suppose, there is some way in this library to put a map "map.png" in the background of the plot. If I would use Python, this problem will be solved like this:
# here we got a map image;
img: Image.Image = Image.open("map-image.jpg")
img.putalpha(64)
imgplot = plt.imshow(img)
# let's pretend that we got our map size in pixels and coordinates
# just in right relation to each other.
scale = 1000
x_shift = 48.0
y_shift = 50.0
coor_a = Coordinate(49.1, 50.4)
coor_b = Coordinate(48.9, 51.0)
x_axis = [coor_a.x, coor_b.x]
x_axis = [(element-x_shift) * scale for element in x_axis]
y_axis = [coor_a.y, coor_b.y]
y_axis = [(element-y_shift) * scale for element in y_axis]
plt.plot(x_axis, y_axis, marker='o')
plt.show()
Desired result on Python
Well, that's easy on Python, but I got no idea, how to do similar thing on Rust.
I am trying to create a cluster point on a map using. This is the code i have used but i am getting the error "list indices must be integers or slices, not float"
# Create map
map_clusters = folium.Map(location=[kol_lat, kol_lng], zoom_start=11)
# Set color scheme for the clusters
x = np.arange(kclusters)
ys = [i + x + (i*x)**2 for i in range(kclusters)]
colors_array = cm.rainbow(np.linspace(0, 1, len(ys)))
rainbow = [colors.rgb2hex(i) for i in colors_array]
# Add markers to the map
markers_colors = []
for lat, lon, poi, cluster in zip(kolkata_merged['Latitude'], kolkata_merged['Longitude'], kolkata_merged['Neighbourhood'], kolkata_merged['Cluster Labels']):
label = folium.Popup(str(poi) + ' (Cluster ' + str(cluster + 1) + ')', parse_html=True)
map_clusters.add_child(
folium.features.CircleMarker(
[lat, lon],
radius=5,
popup=label,
color=rainbow[cluster-1],
fill=True,
fill_color=rainbow[cluster-1],
fill_opacity=0.7))
map_clusters
Make sure there are no NaN values in your dataframe.
Then try using color=rainbow[int(cluster)-1],
The imguR package seems to be exactly what I want, but a scan of the docs makes it seem like it only uploads files. Can I upload the current plot device without outputting to a file? Specifically I'm interested in uploading plots created with ggplot2. Here is what I tried:
> token = imgur_login()
> ggplot(dstat, aes(x=obs,y=tcp)) + geom_line() + theme_bw()
> upload_image(token=token)
Error in file.exists(file) : argument "file" is missing, with no default
EDIT: I should add, I'm comfortable with a method that invisibly creates a file in the background then uploads it, this is just for quick showcasing of plots not for perfect presentation
EDIT: Here is the code I have created so far. It does not work well because there seems to be a cache used inside of account_albums that is not easily disabled. Any help finalizing this would be appreciated
get_or_create_album = function(token) {
# I can find no way to disable the cache inside
# account_albums - it always returns the first
# result unless you login again.
# So if this function creates the album, then
# the user must manually log out and log back in
# or this function will happily continue creating the album
albums = account_albums(ids=F,token=token)
albums = data.frame(t(sapply(albums,c)))
album = albums$id[albums$title == "R ggplot2"]
if (length(album) >= 1)
return(album[[1]])
album = create_album(title='R ggplot2',description='Automatic uploads from ggplot2',privacy='public', token=token)
return(album$id)
}
ggupload = function(token) {
albumID = get_or_create_album(token)
x = tempfile(fileext = '.pdf')
ggsave(filename=x)
p = last_plot()
title = ""
desc = ""
labels = names(p$labels)
if ("x" %in% labels && "y" %in% labels)
title = paste(p$labels$x,"vs",p$labels$y)
desc = title
if ("title" %in% labels)
title = p$labels$title
imgur_upload(file = x, album = albumID, title=title, description = desc, token = token)
}
This works as follows
> token = imgur_login()
> ggplot(dstat, aes(x=obs,y=tcp)) + geom_line() + theme_bw()
> ggupload(token)
I'm using rCharts to create a scatterplot that displays ratings that I have calculated over time. I have more information for each individual data point (rating) and would like to have each data point on the graph link to a unique page with more information about that specific data point.
For example: I would like to be able to hover over the first data point on the graph and click on it to go to a specific page (http://www.example.com/info?id=1) that provides more information about that rating or data point. Each data point has a unique id and unique url that I would like to link to.
Here is the code that I am using to generate the graph
library(rCharts)
age <- c(1:20)
tall <- seq(0.5, 1.90, length = 20)
name <- paste(letters[1:20], 1:20, sep = "")
df <- data.frame(age = age, tall = tall, name = name)
n1 <- nPlot(age ~ tall ,data = df, type = "scatterChart")
n1$xAxis(axisLabel = "the age")
n1$yAxis(axisLabel = "the tall", width = 50)
n1$chart(tooltipContent = "#! function(key, x, y, e ){
var d = e.series.values[e.pointIndex];
return 'x: ' + x + ' y: ' + y + ' name: ' + d.name
} !#")
n1
This should definitely be considered a hack for now, but it works. Issues that we face here that cause us to require this hack are the draw function in the standard rCharts template does not offer us a place to add bits of code for nvd3, and the afterScript for nvd3 falls outside of our draw so is called before the chart is rendered. Also, the nvd3 tooltip is just html, but the problem with providing a link here to click is that mouseover is triggered and the tooltip disappears before we can click on it (fun trick but not useful). So, in this hack we will hijack the tooltip content function to also specify a click event function.
I tried to be clear with comments, but please let me know if none of this makes sense. I certainly do not make a career out of support :), so I have not built up that skill set.
library(rCharts)
age <- c(1:20)
tall <- seq(0.5, 1.90, length = 20)
name <- paste(letters[1:20], 1:20, sep = "")
#this next line is not entirely necessary if other data
#provides the part of the link address
#will also comment in the js piece below to show
#how to handle that
links <- paste0("http://example.com/",name)
df <- data.frame(age = age, tall = tall, name = name, links = links)
n1 <- nPlot(age ~ tall ,data = df, type = "scatterChart")
n1$xAxis(axisLabel = "the age")
n1$yAxis(axisLabel = "the tall", width = 50)
n1$chart(tooltipContent = "#! function(key, x, y, e ){
d3.selectAll('[class*=\"nv-path\"]').on('click',function(){
//uncomment debugger if you want to see what you have
//debugger;
window.open(d3.select(this).datum().data['point'][4].links,'_blank');
//as stated in the r code generating this
//the link address might be in the data that we already have
//window.open(
// 'http://example.com/' + d3.select(this).datum().data['point'][4].name,
// '_blank'
//);
})
//looks like the actual point is below the hover tooltip path
//if tooltips disabled we could do click on the actual points
//d3.selectAll('.nv-group circle').on('click',function(){
// debugger;
//})
var d = e.series.values[e.pointIndex];
return 'x: ' + x + ' y: ' + y + ' name: ' + d.name
} !#")
n1
I hope it helps.
I've searched SO, Googled, read ?pdf, and come up dry as to the possibility of saving a plot as a pdf with layers that can be swtiched on and off in the pdf viewer margins. An example of what I'm talking about are the USGS quad topo-maps, which can be downloaded as pdfs with multiple layers, such as this (zipped pdf).
The following sentence in the pdf() help file sounds ominous, but I also wanted to check that I'm not misinterpreting it:
The R graphics model does not distinguish graphics objects at the level
of the driver interface.
I used to be able to save layered pdf's in Illustrator, but no longer have this program at my disposal. Perhaps someone can think of a workaround from within R? The data I'm using to map are large, but here's a toy example:
pdf("2objects.pdf")
plot(NULL, type = "n",xlim = c(0,1),ylim = c(0,1))
rect(0,.7,.7,0,border = "blue",lwd=2)
rect(.3,1,1,.3,border = "red",lty=2,lwd=2)
dev.off()
It looks like this (it's a png, but the above will give a pdf)
I'd like to be able to have the red and blue boxes as layers with visibility that can be switched on and off from within the pdf viewer.
Many thanks!
Edit: found thread in R-help (re: #mnel), and it looks to not be possible. I will still leave this question open, in case someone has come up with a nifty R-tastic workaround.
Edit (Sept 5th, 2012): I tried doing this via Sweave, and achieved partial success using the workaround posted here. This method produces a single pdf with 'layers' that can be switched on and off using hyperlinked text below the images. It uses 'animation' trickery to do so. While it is still not my ultimate desired outcome, it has the advantage of not depending on particular pdf viewers. I will still wait to see if someone posts a way to do layers, aka OCGs in a Sweave document, which I could then automate.
Edit (Sept 13, 2012): I posted my progress so far as an answer, using the code mentioned above. I was able to get it working in a more complex real world situation with no alterations to the code with overlays of different administrative and statistical boundaries within the US. In this case, I just named the different map overlays layer-0.pdf, layer-1.pdf, etc, and it worked without error. I still hope something better pops up here eventually.
Thanks all for you comments
I'm able to achieve this via ggplot.
library(ggplot2)
df <- data.frame(x = c(1,10), y = c(20,40), class = 1:2)
layered_plot <- ggplot(df, aes(xmin = x, xmax = x + 1, ymin = y, ymax = y + 2, fill = class)) +
geom_rect() +
opts(legend.position = "none")
# Now save this as pdf
ggsave(layered_plot, file="p1.pdf")
(This is just the png version for illustration but when I open the pdf in Illustrator, I can turn off the individual layers as needed).
Looks like the (tex) animation answer is the best I can come up with now. The following .Rnw file will create a pdf with a figure in the middle, and 2 text hyperlinks below it, which toggle visibility of the red and blue boxes independently. I found the Tex code that makes this work here. I've not looked at #Aaron's ocgtools suggestion yet, but will get there. Thanks all for your suggestions!
\documentclass{article}
%----------------------------------------------------------------%\
\usepackage[OT1]{fontenc}
\usepackage{Sweave}
\usepackage{animate}
\usepackage{hyperref}
\usepackage[margin=0.4in]{geometry}
%----------------------------------------------------------------%
\makeatletter
% command to create a toggle link
\newcommand{\ShowHideLayer}[3]{%
% #1: anim No. (zero-based),
% #2: layer No. (zero-based),
% #3: link text
\leavevmode%
\pdfstartlink user {
/Subtype /Link
/Border [\#pdfborder]%
/A <<
/S/JavaScript
/JS (
\if at anim#useocg%
if(a#1.fr[#2].state==true){
a#1.fr[#2].state=false;
}else{
a#1.fr[#2].state=true;
}
\else
if (a#1.fr[#2].display==display.visible){
a#1.fr[#2].display=display.hidden;
}else{
a#1.fr[#2].display=display.visible;
}
this.dirty=false;
\fi
)
>>
}#3%
\pdfendlink%
}
% command to create a link to show/hide all layers
\newcommand{\ShowHideAll}[2]{%
% #1: anim No. (zero-based),
% #2: link text
\leavevmode%
\pdfstartlink user {
/Subtype /Link
/Border [\#pdfborder]%
/A <<
/S/JavaScript
/JS (
var countvisible=0;
for(var i in a#1.fr){
\if at anim#useocg
if(a#1.fr[i].state==true){countvisible++;}
\else
if (a#1.fr[i].display==display.visible){countvisible++;}
\fi
}
if(countvisible){
for(var i in a#1.fr){
\if at anim#useocg
a#1.fr[i].state=false;
\else
a#1.fr[i].display=display.hidden;
this.dirty=false;
\fi
}
}
else{
for(var i in a#1.fr){
\if at anim#useocg
a#1.fr[i].state=true;
\else
a#1.fr[i].display=display.visible;
this.dirty=false;
\fi
}
}
)
>>
}#2%
\pdfendlink%
}
\makeatother
\begin{document}
% heres the R-making of the plots, saved to working directory,
% which should be the folder containing this .Rnw file
% 3 versions of the same plot, one for each layer
<<echo = FALSE, hide = TRUE>>=
pdf("layer-0.pdf")
plot(NULL, type = "n", xlim = c(0, 1), ylim = c(0, 1), xlab = "", ylab = "")
dev.off()
pdf("layer-1.pdf")
plot(NULL, type = "n", xlim = c(0, 1), ylim = c(0, 1), axes = FALSE, xlab = "", ylab = "")
rect(0, .7, .7, 0, border = "blue", lwd = 2)
dev.off()
pdf("layer-2.pdf")
plot(NULL, type = "n", xlim = c(0, 1), ylim = c(0, 1), axes = FALSE, xlab = "", ylab = "")
rect(.3, 1, 1, .3, border = "red", lty = 2, lwd = 2)
dev.off()
#
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\begin{center}
%animated layer-set No. 0
% v-- frame rate ignored
\animategraphics[width=1\linewidth,step]{1}{layer-}{0}{2}
\ShowHideLayer{0}{1}{toggle red box}\\
\ShowHideLayer{0}{2}{toggle blue box}\\
\end{center}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\end{document}