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

Display non-geographical maps ala leafletjs #67

Closed
vbonhomme opened this issue Dec 12, 2017 · 10 comments
Closed

Display non-geographical maps ala leafletjs #67

vbonhomme opened this issue Dec 12, 2017 · 10 comments

Comments

@vbonhomme
Copy link

Hi and thanks a lot for the awesome job here (and elsewhere).

I have no idea where to ask this question but I try here. Apologies if I should ask to leaflet for R or elsewhere.

I have struggling hard and for long to develop a digitizing software for morphometrics. Long story short: it would be widely used and mapview/edit are more than starters.

One blocking aspect for this now is to properly display images using pixels coordinates. So far, I manage to display any image using raster with xmx= 1 (and the ratio for ymx) but that's far from being clean (ie we need to retransform into pixels, scales are wrong, etc.).

I would pretty much need what's described in the leafletjs doc for non-geographical maps. On the leaflet() side, L.CRS.Simple seems to be the way to go, but, for such raster, we still need a projection. I have tried for a week and I nearly #made no progress. So, dear gurus, any idea how can we circumvent this problem and project an image (as a raster) in pixels coordinates and on a cartesian plan? A reproducible and minimal bit of code below if this may help.

Thanks a lot for your time and skills

# dependencies
library(jpeg)
library(raster)
library(leaflet)
library(leaflet.extras)
library(mapview)
library(mapedit)

# dl an image
path <- "https://upload.wikimedia.org/wikipedia/commons/thumb/5/58/CheHigh.jpg/280px-CheHigh.jpg"
che <- tempfile()
download.file(path, che, mode="wb")
im <- jpeg::readJPEG(che)

# prepare a raster from it
r  <- raster(xmn=0, xmx=1,
             ymn=0, ymx=nrow(im)/ncol(im),
             ncols=ncol(im), nrows=nrow(im))
r <- setValues(r, as.numeric(t(im)))

# prepare the leaflet
lf <- leaflet() %>% addTiles() %>% addRasterImage(r)
# view it
editMap(lf)
@vbonhomme
Copy link
Author

vbonhomme commented Dec 13, 2017

also asked the question in SO, there

@tim-salabim
Copy link
Member

tim-salabim commented Dec 13, 2017

See also these two threads

rstudio/leaflet#371
#54

leaflet currently does not render raster images with arbitrary or no crs. In an early version of mapview we circumvented this with the approach you mention via scaling the coordinates to lie between 0 and 1. However, I agree that this is not a solution, especially for digitizing.
One hope I have is that whenever leaflet gets updated to leafletjs 1.x this issue will be resolved. @timelyportfolio have you ever tested whether rasters work with L.CRS.Simple in the leaflet 1.x branch? Or would it be possible to enable editMap to work with mapview::plainview which is used when native.crs = TRUE?

mapview(r, native.crs = TRUE)

@vbonhomme
Copy link
Author

vbonhomme commented Dec 14, 2017

Hi @tim-salabim and @timelyportfolio (me again!)

With mapview(r, native.crs=TRUE) I got the (seen-elsewhere) following error:

Error in round(size) : non-numeric argument to mathematical function
In addition: Warning message:
In rasterCheckSize(x, maxpixels = maxpixels) :
  maximum number of pixels for Raster* viewing is #BEBEBE80 ; 
the supplied Raster* has 100520 
 ... decreasing Raster* resolution to #BEBEBE80 pixels
 to view full resolution set 'maxpixels =  100520 '

plainview(r)works though, and if we could do the same things that we do with a regular editmap I think the main problem would be solved!

@tim-salabim
Copy link
Member

tim-salabim commented Dec 14, 2017

This has been fixed recently, see r-spatial/mapview#111
So if you install mapview from github you should be able to use native.crs = TRUE

devtools::install_github("r-spatial/mapview@develop")

@vbonhomme
Copy link
Author

indeed!

@vbonhomme
Copy link
Author

@timelyportfolio may I ping you to have your feeling about what @tim-salabim suggested? Cheers!

@timelyportfolio
Copy link
Contributor

Sure, feel free to ping me. Might be a little slower in responding over the holidays. Cheers!

@vbonhomme
Copy link
Author

@timelyportfolio , that was a question, perhaps too politely asked ;-)

@timelyportfolio
Copy link
Contributor

timelyportfolio commented Dec 29, 2017

Ok, I understand now. I very likely am headed down the wrong path, so feel free to set me straight.

  1. addRasterImage() creates a tile layer. Is this necessary, or is the L.imageOverlay as in example sufficient?

  2. This might be helpful down the road When using L.CRS.Simple for the map, drawing a circle creates a really huge unusable circle Leaflet/Leaflet.draw#611.

Some code as a demonstration of the very little that I accomplished.

library(jpeg)
library(raster)
library(leaflet)
library(leaflet.extras)
library(mapview)
library(mapedit)

# dl an image
path <- "https://upload.wikimedia.org/wikipedia/commons/thumb/5/58/CheHigh.jpg/280px-CheHigh.jpg"
che <- tempfile()
download.file(path, che, mode="wb")
im <- jpeg::readJPEG(che)

# prepare a raster from it
r  <- raster(xmn=0, xmx=1,
             ymn=0, ymx=nrow(im)/ncol(im),
             ncols=ncol(im), nrows=nrow(im))
r <- setValues(r, as.numeric(t(im)))

# prepare the leaflet
lf <- leaflet(
  options = leafletOptions(
    crs = leafletCRS("L.CRS.Simple"),
    minZoom = 1,
    maxZoom = 10
  )
) %>%
  setView(0, 0, zoom=3)

lf2 <- htmlwidgets::onRender(
  lf,
sprintf(
"
  function(el, x) {
    var map = this;
    var w = %s,
        h = %s,
        url = 'http://upload.wikimedia.org/wikipedia/commons/thumb/5/58/CheHigh.jpg/280px-CheHigh.jpg';
    
    // calculate the edges of the image, in coordinate space
    var southWest = map.unproject([0, h], 3);
    var northEast = map.unproject([w, 0], 3);
    var bounds = new L.LatLngBounds(southWest, northEast);

    L.imageOverlay(url, bounds).addTo(map);
  }
",
  dim(im)[2],
  dim(im)[1]
)
)

editMap(lf2)

leaflet.extras uses layer.toGeoJSON() which does not gives projected coordinates unfortunately (or at least I think unfortunately). I believe we might need map.latLngToLayerPoint instead.

I know this isn't much, but maybe it will get us one step further towards something usable.

@vbonhomme
Copy link
Author

Thanks a lot, that unlocks some key aspect of my problem ! I'll dig it further and let you know.

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

No branches or pull requests

3 participants