ramnathv / rMaps

Interactive Maps from R
http://rmaps.github.io
389 stars 194 forks source link

Choropleth maps using custom layer #6

Closed tyokota closed 10 years ago

tyokota commented 10 years ago

I recently came across http://rmaps.github.io/blog/posts/animated-choropleths/. Is it possible for rMaps to load a custom shape file for the base layer? Thanks!

ramnathv commented 10 years ago

You will need to convert the shapefile to the topojson format, which can then be passed to datamaps, which is the underlying javascript library behind that plot created using rMaps. Being able to handle multiple mapping formats automatically is the one of the goals of rMaps, but it will take us some time to get there.

tyokota commented 10 years ago

I'll give it a shot. If I am able to convert the shapefile, what is the map$set attribute to call the topojson file?

ramnathv commented 10 years ago

You will need to specify two arguments, dataUrl and setProjection. I would recommend that you read this wiki post on the datamaps repo to understand how this works.

In rMaps, you can write the code below.

map$set(geographyConfig = list(
  dataUrl = dataUrl,
  setProjection = "#! function(...){

  }) !#"
))

I will try to post an example later on using custom maps, but you should be able to figure this out based on my note and the datamaps article. Let me know if you face any trouble.

tyokota commented 10 years ago

Thanks, Ramnath! I'll give it a shot. I'll close this issue for now as I think you've provided enough information to help me on my way.

ramnathv commented 10 years ago

@tyokota if you can write up a post on how to use custom maps with rMaps, I would be happy to guest post it on my rMaps blog :)

lcolladotor commented 10 years ago

Hello,

After learning about rMaps yesterday, I got curious and thought of of making a ichoropleth() of Mexico's crime. I don't much about this! --- yesterday I read about topojson for the 1st time. So, I'll just add links to things I found out googling which might help others later as well as helping me keep track of these sites.

General

Maybe helpful?

Specific to Mexico

@diegovalle has posted several analyses of Mexico's crimes supplying both the javascript and the R code he used. For example, he used Leaflet to make a density map of homicides in Monterrey. Check more at his bl.ock, his highly complete analysis of the drug war in Mexico via GitHub, his website. In particular, his narcomap looks very cool! He described it in this post. His Mexico crime report looks very similar to knitrBootstrap reports.

@ramnathv maybe @diegovalle is interested on helping you out with rMaps. You never know!

Previously made maps

They've clearly put a lot more effort into those maps than what I was thinking of investing. But well, @tyokota, if you figure out how to make custom rMaps let us know! --- I don't know javascript, so I don't know how the setProjection function should look like (even after reading the datamaps wiki).

Cheers, Leo

tyokota commented 10 years ago

I wasn't very successful myself. This is what I came up with based upon the Wiki:

map <- Datamaps$new()
map$set(
  legend = TRUE,
  labels = TRUE,
  geographyConfig = list(
    dataUrl = "/gis/uk.json", 
    setProjection = "#!function(element, options) {
      var projection = d3.geo.albers()
          scale(element.offsetWidth)
          .translate([element.offsetWidth / 2, element.offsetHeight / 2]);

      var path =  d3.geo.path()
              .projection(projection);

      return {path: path, projection: projection};
  }
  !#"),
    fills = fills
)
map
ramnathv commented 10 years ago

It is trickier than I thought. I am working on an example using the mexico maps that were linked to by @lcolladotor. Once I have a reasonable working prototype, I will post it.

tyokota commented 10 years ago

Thanks, Ramnath. Looking forward to it. Anyway I can help? PayPal a beer? :)

ramnathv commented 10 years ago

Hehe. A blog post outlining details of how to do custom maps would be good :)

tyokota commented 10 years ago

Sure, if you can help with the last step, then it's a deal!

ramnathv commented 10 years ago

DONE! Make sure that you don't use the rawgithub link for a production map. Note that the hover doesn't work since the mx_tj.json file does not seem to have any properties or names. You will need to find or create an appropriate topojson file for hover to show up.

library(rMaps)
d1 <- Datamaps$new()
d1$set(
  geographyConfig = list(
    dataUrl = "https://rawgithub.com/tjhladish/g3_dengue_map/5570dc18d675d63b02929fa4855ba73922fdfcd9/mx_tj.json"  
  ),
  scope = 'estados2',
  setProjection = '#! function( element, options ) {

    var projection, path;

      projection = d3.geo.mercator()
      .center([-89, 21])
      .scale(element.offsetWidth)
      .translate([element.offsetWidth / 2, element.offsetHeight / 2]);

    path = d3.geo.path()
      .projection( projection );

    return {path: path, projection: projection};
  } !#'
)
tyokota commented 10 years ago

Thanks, Ramnath! I will take a look as soon as my commute home is finished. I can create a toy example from a project I started. I will create a blog post based upon this ASAP. Thank you again!

ramnathv commented 10 years ago

You are welcome. You might have to create a custom topojson file for mexico, that includes the state names. There are some excellent tutorials by Mike Bostock on how to do this using a few command line tools.

ramnathv commented 10 years ago

Just figured out how to create mexico.topo.json with properties. Run the following lines from your command line.

wget http://biogeo.ucdavis.edu/data/diva/adm/MEX_adm.zip
unzip MEX_adm.zip
ogr2ogr -f GeoJSON subunits.json MEX_adm1.shp
topojson --id-property adm1_code -p -o mexico.topo.json subunits.json
tyokota commented 10 years ago

I was able to get rMaps to display my custom layer that utilized the fill key. I still saw an undefined when hovering above. Do I need a column for the places in the topoJSON file? Does it have to be titled "name"?

d1 <- Datamaps$new()
d1$set(
  geographyConfig = list(
    dataUrl = "https://dl.dropboxusercontent.com/u/6581521/house.topo.json"  
  ),
  scope = "house",
  setProjection = '#! function( element, options ) {

  var projection, path;

  projection = d3.geo.mercator()
  .center([-157.86, 21.30])
  .scale(3000)
  .translate([element.offsetWidth / 2, element.offsetHeight / 2]);

  path = d3.geo.path()
  .projection( projection );

  return {path: path, projection: projection};
  } !#',
  fills = fills,
  data = dat2[[1]],
  legend = TRUE,
  labels = TRUE
)
d1
ramnathv commented 10 years ago

Inside the geographyConfig list, you need to add a popupTemplate as well. Your house.json has only one property listed, which is HDist, so that will be the only one displayable. You will need a topojson file with more properties if you want to display more.

d1$set(
  geographyConfig = list(
    dataUrl = "https://dl.dropboxusercontent.com/u/6581521/house.topo.json" ,
    popupTemplate =  "#! function(geography, data) { //this function should just return a string
          return '<div class=hoverinfo><strong>' + geography.properties.HDist + '</strong></div>';
    }  !#", 
  ),
  ...
)
tyokota commented 10 years ago

Thank you, Ramnath. Here's my attempt to try and explain the process: https://github.com/tyokota/custom-map.

lcolladotor commented 10 years ago

@tyokota Looks very complete! http://rawgithub.com/tyokota/custom-map/master/custom-map.html

I'm just curious why you hide the setup R code chunk. Some of the options, like options(rcharts.cdn = TRUE) could be useful to know.

As for the javascript code, I understand empirically that the javascript code has to be contained inside the "#! and !#" tags. I can see the links to the datamaps wiki, but well, I don't fully understand yet how you determine which parts of the javascript code you need to change.

I guess that in the end I do need to learn some javascript.

diegovalle commented 10 years ago

I've created an animated choropleth of homicide rates using the Mexican data @lcolladotor mentioned. If you just want to create the topojson you can run the following commands:

curl -o estados.zip http://mapserver.inegi.org.mx/MGN/mge2010v5_0.zip
unzip estados.zip 
ogr2ogr states.shp Entidades_2010_5.shp -t_srs "+proj=longlat +ellps=WGS84 +no_defs +towgs84=0,0,0"
## id-property needed so that DataMaps knows how to color the 
topojson -o mx_states.json -s 1e-7 -q 1e5 states.shp -p state_code=+CVE_ENT,name=NOM_ENT --id-property NOM_ENT
ramnathv commented 10 years ago

This really cool! I packaged all the code in my blog post into an ichoropleth function that should ideally be able to do this in one line of code, once you have the data in the right format.

ramnathv commented 10 years ago

As I suspected, you could do this with ichoropleth. Here is the code. You will need to start a webserver to view map.html. You can experiment with options. For example, if you pass the argument play = TRUE to ichoropleth, you will get a play button that allows you to loop through the years automatically. In the next version, I will simplify the projection function and allow users to just pass a list with the center, since that is really the main option that differs, and possibly the name of the projection.

Let me know if you run into any trouble implementing this. If you write a blog post, do let me know, and also the author of datamaps, who is responsible for most of the credit behind this tool.

d1 <- ichoropleth(rate ~ name, data = hom, ncuts = 9, pal = 'YlOrRd', 
    animate = 'year',  map = 'states'
)
d1$set(
  geographyConfig = list(
   dataUrl = "mx_states.json"
  ),
 scope = 'states',
 setProjection = '#! function( element, options ) {
   var projection, path;
   projection = d3.geo.mercator()
    .center([-89, 21]).scale(element.offsetWidth)
    .translate([element.offsetWidth / 2, element.offsetHeight / 2]);

   path = d3.geo.path().projection( projection );
   return {path: path, projection: projection};
  } !#'
)
d1$save('map.html', cdn = TRUE)
lcolladotor commented 10 years ago

Great work everyone!! Impressive!

lcolladotor commented 10 years ago

I'm just curious about the colors of both @diegovalle and @ramnathv's maps. They look very different despite basically using the same groups for the homicide rates.

ramnathv commented 10 years ago

Diego's map defaults to 2013, while mine defaults to 2007 (I think). Plus, the groupings are different. ichoropleth uses quantiles, while @diegovalle is using the cut function and breaks. It makes sense to add more flexibility to ichoropleth.

lcolladotor commented 10 years ago

Blogged about the series here

ramnathv commented 10 years ago

@lcolladotor Very nice post. Thanks for doing this. If you tweet about it, I can RT and get the word out. Figuring out the allow-access-control trick is great. If you want to add a line or two on how you managed it, would be useful. Great work again!

lcolladotor commented 10 years ago

Thanks and no problem @ramnathv =)

About the allow-access-control, I simply noted that @tyokota didn't run into this problem when hosting the json in Dropbox. So I simply dodged the issue by using Dropbox too.

AritraBiswas commented 9 years ago

Hi, I’m not being able to understand this part the code: “-s 1e-7 -q 1e5 states.shp -p state_code=+CVE_ENT,name=NOM_ENT --id-property NOM_ENT” Please help me to figure it out. I want to create a map of India using rMaps.

diegovalle commented 9 years ago

@AritraBiswas Hi, those commands are to simplify and preserve features of the shapefiles. You can consult the full command reference from the TopoJSON website

endri81 commented 9 years ago

I am having a lot of troubles. Tried already http://geojson.io/ to create topojson and many other websites. My shp files was with projection d3.geo.transverseMercator and still nothing shown :(

library(rMaps) d1 <- Datamaps$new() d1$set( geographyConfig = list( dataUrl = "https://www.dropbox.com/s/axn2qdic63hqvbu/map.topojson?dl=0"
), scope = 'alb', setProjection = '#! function( element, options ) {

var projection, path;

projection = d3.geo.mercator() .center([-89, 21])

ridhimagupta1191 commented 9 years ago

Hi,

I have been trying to create a custom map for India but the code runs well without displaying any map at the end. Can you please help me out with it ?

options(rcharts.cdn = TRUE) d1 <- Datamaps$new() d1

d1$set( geographyConfig = list( dataUrl = "IND_adm1.json", popupTemplate = "#! function(geography, data) { //this function should just return a string return '

' + geography.properties.NAME_1 + '
'; } !#" ), dom = 'chart_1', scope = 'NAME_1', labels = TRUE, bodyattrs = "ng-app ng-controller='rChartsCtrl'", setProjection = '#! function( element, options ) {

var projection, path;

projection = d3.geo.mercator() .center([-89, 21]) .scale(element.offsetWidth) .translate([element.offsetWidth / 2, element.offsetHeight / 2]);

path = d3.geo.path() .projection( projection );

return {path: path, projection: projection}; } !#', fills = fills, data = dat2[[1]], legend = TRUE, labels = TRUE ) d1

ekstroem commented 9 years ago

Try to make the necessary files available and I'll give it a look

ridhimagupta1191 commented 9 years ago

https://drive.google.com/folderview?id=0B0dws70OAX9uM1k4aHJXM0lOalU&usp=sharing

ekstroem commented 9 years ago

Well I can confirm that I get a blank page just like you when I run your files. Are you sure the JSON file is in the correct format? How did you produce it? Maybe open another issue specific to your problem?