Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add Leaflet.heatmap #174

Closed
wants to merge 10 commits into from

Conversation

timelyportfolio
Copy link
Contributor

In response to #137, add Leaflet.heat to leaflet. While I think the implementation is complete, I would like some input on how I can make the gradient argument more R-like or consistent with leaflet. As of now, it looks like this

 list(
      '0.2' = '#ffffb2',
      '0.4' = '#fd8d3c',
      '0.6' = '#fd8d3c',
      '0.8' = '#f03b20',
      '1' = '#bd0026'
  )

Some examples are in the ./inst/examples/heatmap.r directory and also included in the documentation. I'll paste here also.

library(leaflet)

# match the heatmap demo from Leaflet.heat
# https://github.com/Leaflet/Leaflet.heat/blob/gh-pages/demo/index.html

# get the data to match exactly
addressPoints <- readLines(
  "http://leaflet.github.io/Leaflet.markercluster/example/realworld.10000.js"
)
addressPoints <- apply(
  jsonlite::fromJSON(
    sprintf("[%s]",
      paste0(
        addressPoints[4:(length(addressPoints)-1)]
        ,collapse=""
      )
    )
  )
  ,MARGIN = 2
  ,as.numeric
)

# create our heatmap
leaf <- leaflet() %>%
  setView( 175.475,-37.87, 12 ) %>%
  addHeatmap(addressPoints)

leaf

# customize our heatmap with options
library(RColorBrewer)
pal <- brewer.pal(9,"BuPu")

leaf <- leaflet() %>%
  setView( 175.475,-37.87, 12 ) %>%
  addHeatmap(
    addressPoints
    ,blur = 50
    # using example
    # https://esri.github.io/esri-leaflet/examples/styling-heatmaps.html
    ,gradient = list(
      '0.2' = '#ffffb2',
      '0.4' = '#fd8d3c',
      '0.6' = '#fd8d3c',
      '0.8' = '#f03b20',
      '1' = '#bd0026'
    )
  )

leaf

# replicate the example provided by
#   http://www.d3noob.org/2014/02/generate-heatmap-with-leafletheat-and.html

earthquakes <- readLines(
  "http://bl.ocks.org/d3noob/raw/8973028/2013-earthquake.js"
)
earthquakes <- apply(
  jsonlite::fromJSON(
    sprintf("[%s]",
            paste0(
              earthquakes[5:(length(earthquakes)-1)]
              ,collapse=""
            )
    )
  )
  ,MARGIN = 2
  ,as.numeric
)

leaflet() %>%
  addTiles() %>%
  setView( 174.146, -41.5546, 10 ) %>%
  addHeatmap(
    earthquakes,
    radius = 20,
    blur = 15,
    maxZoom = 17
  )

#  using data(quakes)
data(quakes)
quakes_mat <- matrix(t(quakes[,c(1:2,4)]),ncol=3,byrow=T)
leaflet() %>%
  addTiles( ) %>%
  setView( 178, -20, 5 ) %>%
  addHeatmap( quakes_mat, blur = 30, max = 0.05, radius = 30 )

# to remove the heatmap
leaf %>% clearHeatmap()

image

@jcheng5
Copy link
Member

jcheng5 commented Aug 27, 2015

Is the range of the gradient always 0-1?

@jcheng5
Copy link
Member

jcheng5 commented Aug 27, 2015

If the range of the gradient is always 0-1, then instead of a gradient you could take a color function, like other leaflet functions do.

leaflet() %>%
  addTiles( ) %>%
  setView( 178, -20, 5 ) %>%
  addHeatmap( quakes_mat, blur = 30, max = 0.05, radius = 30,
    color = colorNumeric("RdYlBu", 0:1)
  )

Then inside addHeatmap, do something like

gradient <- as.list(color(0:20 / 20))
names(gradient) <- as.character(0:20 / 20)


# using data(quakes)
data(quakes)
quakes_mat <- matrix(t(quakes[,c(1:2,4)]),ncol=3,byrow=T)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you change T to TRUE here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

embarrassed that I let that through; changed

@jcheng5
Copy link
Member

jcheng5 commented Aug 28, 2015

Kenton, can I trouble you to not use the leading-comma style in this repo?

foo(
  bar
  ,baz
  ,qux
)

I generally try to be extremely laissez-faire about code formatting style, but this is going too far afield IMHO. :)

@jcheng5
Copy link
Member

jcheng5 commented Aug 28, 2015

Other than those comments, this looks great! Thanks!

@jcheng5
Copy link
Member

jcheng5 commented Aug 28, 2015

Oh sorry, I missed something quite obvious. If latlngs are just latitude/longitude pairs, then we don't pass them like this in leaflet. See the usage of derivePoints in e.g. addMarkers, which takes separate lng/lat parameters (and unfortunately it is lng then lat).

@jcheng5
Copy link
Member

jcheng5 commented Aug 28, 2015

Just to be crystal clear: addHeatmap should have lng=NULL, lat=NULL, and data=getMapData(map) parameters. Then call

    pts = derivePoints(data, lng, lat, missing(lng), missing(lat), 
        "addHeatmap")

and pass pts$lat and pts$lng to invokeMethod.

@timelyportfolio
Copy link
Contributor Author

@jcheng5, hope it is closer now. I tried to follow what I found on the JavaScript and R side for consistency.

@timelyportfolio
Copy link
Contributor Author

Not sure that is how the max works. By leaving 0:1, max is the only way to scale the intensity, so going to leave 0:1. The alt trick didn't work for me. Leaflet.heat seems to want an array unless I'm just screwing up. That is great to know on the auto-resolve. Neat little bit of sugar.

@jcheng5
Copy link
Member

jcheng5 commented Aug 28, 2015

@timelyportfolio Do you have time to vchat some time today or Monday? I'm very unclear on the semantics of intensity vs. max, even after studying the source code of Leaflet.Heat and simpleheat. Frankly I'm not sure the Leaflet.Heat behavior is correct/desirable. Can I sketch out what I'm seeing, you can tell me your understanding, and we'll see if we can make sense of it?

@timelyportfolio
Copy link
Contributor Author

@jcheng5 happy to vchat, but I have to admit, I am at a loss also. It is odd and not documented. All I can determine is that reducing the max increases the color intensity. Free anytime. What is your preferred method? I'll spend some more time with it since I plan to make a simpleheat widget also.

@timelyportfolio
Copy link
Contributor Author

@jcheng5 this v transform in line means our max will be much less than 1. Locally, I've added the non-minified. If it helps, I can commit to a diff branch for debugging.

@timelyportfolio
Copy link
Contributor Author

Take for example the quakes example and supply a multiplier to the mag.

data(quakes)
data(quakes)

leaflet(quakes) %>%
  addTiles( ) %>%
  setView( 178, -20, 5 ) %>%
  addHeatmap( lng = ~long, intensity = ~mag,
              blur = 5, radius = 2,
              gradient = "Greys" )

yields a max in the data supplied to simpleheat of 0.051.

leaflet(quakes) %>%
  addTiles( ) %>%
  setView( 178, -20, 5 ) %>%
  addHeatmap( lng = ~long, intensity = ~mag*1000,
              blur = 5, radius = 2,
              gradient = "Greys" )

gives us a max of 1.

I guess if we want to be smart :) we could calculate the k on the R side and try to scale the intensity values to [0,1].

@timelyportfolio
Copy link
Contributor Author

With your pull request to Leaflet.heat,I just discovered this very interesting and useful side effect. We can easily create dot maps. As an example,

library(leaflet)

# match the heatmap demo from Leaflet.heat
# https://github.com/Leaflet/Leaflet.heat/blob/gh-pages/demo/index.html

# get the data to match exactly
addressPoints <- readLines(
  "http://leaflet.github.io/Leaflet.markercluster/example/realworld.10000.js"
)
addressPoints <- apply(
  jsonlite::fromJSON(
    sprintf("[%s]",
      paste0(
        addressPoints[4:(length(addressPoints)-1)]
        ,collapse=""
      )
    )
  )
  ,MARGIN = 2
  ,as.numeric
)

addressPoints <- data.frame( addressPoints )
colnames( addressPoints ) <- c( "lat", "lng", "value" )

leaflet( addressPoints ) %>% addTiles() %>%
  setView( 175.475,-37.87, 12 ) %>%
  addHeatmap(intensity=~value, radius = 2, blur = 0, gradient = blues9[9] )

image

I guess addCircles can accomplish the same thing also, so probably just something interesting to note. Howerver, addCircle is slower and less performant, since they are svg.

@timelyportfolio
Copy link
Contributor Author

@jcheng5, there seems to be no movement on your pull request Leaflet/Leaflet.heat#32. I'm very much in favor of using your pull for this plugin for the purposes of this leaflet htmlwidget and iterating based on future use cases, examples, and tests. Otherwise, this seems like it might languish for a while, and I think this is too valuable to sit.

Thanks so much for all your help with this and finding what I think is a very good solution.

Merge branch 'master' of https://github.com/rstudio/leaflet into feature/heatmap

Conflicts:
	NAMESPACE
@smartinsightsfromdata
Copy link

@timelyportfolio @jcheng5 is this going ahead?

It is a very useful plugin...

@bhaskarvk
Copy link
Collaborator

+1 will be nice to have.

@kezhan
Copy link

kezhan commented Jun 23, 2016

This really is an awesome feature ! I noticed that it is not yet integrated into master. Are you planning to do so ? Thank you all.

@odvratnozgodan
Copy link

Hi, great feature, been looking for some time for an interactive map with heatmap support. Do you know when will it be merged into the master branch?

@jcheng5 jcheng5 mentioned this pull request Sep 1, 2016
@bhaskarvk
Copy link
Collaborator

I'm closing this. I'm moving all new plugins to leaflet.extras package.

@bhaskarvk bhaskarvk closed this Oct 4, 2016
@bhaskarvk
Copy link
Collaborator

Heatmap Support via an alternative but more performant plugin https://github.com/ursudio/webgl-heatmap-leaflet Now in trunk of https://github.com/bhaskarvk/leaflet.extras/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants