-
Notifications
You must be signed in to change notification settings - Fork 40
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
support mesh3d data #94
Comments
I had a go at this, converting the quad-type mesh3d into the form I'm confused about whether we need a feature for each geometry or not, and I'm confused about conversion to actual ## convert a 3-column matrix to text embedded triples in json arrays
coord_to_json <- function(x) paste0(unlist(lapply(split(t(x), rep(seq_len(nrow(x)), each = ncol(x))), function(cds) sprintf("[%f,%f,%f]", cds[1], cds[2], cds[3]))), collapse = ",")
## add that first coordinate to the end
coord1 <- function(x) rbind(x, x[1, ])
format_mesh <- function(x, ...) {
UseMethod("format_mesh")
}
#' format mesh3d into JSON suitable for mapdeck ... WIP
format_mesh.mesh3d <- function(x, ...) {
## switch on x$primitivetype == "quad"/"triangle"
stopifnot(x$primitivetype == "quad")
feature_template <- '[{"type":"Feature","properties":{"elevation":0,"fill_colour":"#440154FF","stroke_colour":"#440154FF"},%s}]'
polygon_template <- '"geometry":{"geometry":{"type":"Polygon","coordinates":[[%s]]}}'
## assume the expanded coords for each primitive
coords <- sprintf(polygon_template, unlist(lapply(seq_len(ncol(qm$ib)), function(iquad) coord_to_json(coord1(t(qm$vb[1:3, qm$ib[, iquad] ]))))))
paste0(sprintf(feature_template, coords, collapse = ","), collapse = ",")
}
library(quadmesh)
library(raster)
## dummy raster
rr <- setExtent(raster::raster(matrix(sample(1:12), 3)), raster::extent(0, 4, 0, 3))
qm <- quadmesh(rr * 10000) ## something suitably exaggerated
## confused about conversion to 'json' class ...
format_mesh(qm)
Also, it will definitely save the effort of conversion to sf polygons, but it's essentially in the same form here - 5 coordinates for each quad, rather than a vertex array and primitive-index array - but perhaps this will help us find other ways. |
There are a couple of ways I could implement this, and either here or in spatialwidget. Either way, somewhere the
I think 1) is more maintainable and easiest to implement, but possibly could incur a slight penalty because of the reshaping. However I think this is the approach I'm going to take. @mdsumner for what it's worth, I've got a skeleton quadmesh -> sf cpp converter here on branch issue94. It's by no means complete, and I don't need all the sf construction steps, but I made it anyway to make a start. If you think it's useful for a p <- mapdeck:::rcpp_mesh_geojson( qm )
> p
Geometry set for 12 features
geometry type: POLYGON
dimension: XYZM
bbox: xmin: 0 ymin: 0 xmax: 0 ymax: 0
epsg (SRID): NA
proj4string:
First 5 geometries:
POLYGON ZM ((0 3 -24970 1, 1 3 15029.99 1, 1 2 ...
POLYGON ZM ((1 3 15029.99 1, 2 3 17527.49 1, 2 ...
POLYGON ZM ((2 3 17527.49 1, 3 3 35002.5 1, 3 2...
POLYGON ZM ((3 3 35002.5 1, 4 3 104965 1, 4 2 1...
POLYGON ZM ((0 2 -4966.67 1, 1 2 55021.66 1, 1 ... |
Oh cool thanks! (We definitely need your sf-constructor API, that's a great example for me to learn from - I've found following you elsewhere here has been quite tough. ) Definitely this should be for (quadmesh only adds the projection crs and the original raster spec for limited round-tripping) |
prototype example (I may use library(quadmesh)
library(raster)
## dummy raster
rr <- setExtent(raster::raster(matrix(sample(1:12), 3)), raster::extent(0, 4, 0, 3))
qm <- quadmesh(rr * 10000) ## something suitably exaggerated
library(mapdeck)
set_token( read.dcf("~/.googleAPI", fields = "MAPBOX"))
mapdeck() %>%
add_mesh(
data = qm
, fill_colour = "avg_z"
, vertex = "vb"
, index = "ib"
)
|
And using your hawaii exmaple library(quadmesh)
data("hawaii", package = "marmap")
qm <- hawaii %>%
marmap::as.raster() %>%
raster::aggregate(fact = 2) %>%
quadmesh()
mapdeck() %>%
add_mesh(
data = qm
, fill_colour = "avg_z"
, vertex = "vb"
, index = "ib"
)
|
Triangle example tri2 <- qm
tri2$it <- triangulate_quads(tri2$ib)
tri2$ib <- NULL
tri2$primitivetype <- "triangle"
mapdeck() %>%
add_mesh(
data = tri2
, fill_colour = "avg_z"
, vertex = "vb"
, index = "it"
) |
@mdsumner is there a way to know which of the I'm thinking something akin to the way sf <- spatialwidget::widget_melbourne
attr(sf, "sf_column")
"geometry" where this lets us know the column name of the geometries. Can we infer anything about these attributes to tell us the attributes( qm )
$names
[1] "vb" "ib" "primitivetype" "material" "normals" "texcoords"
[7] "raster_metadata" "crs"
$class
[1] "quadmesh" "mesh3d" "shape3d" |
I could certainly add that to quadmesh, but mesh3d will be more widespread - to me the value of quadmesh()/triangmesh() is that it creates these rgl types from spatial data - though maybe I don't understand? mesh3d will always have |
yeah if this is a consistent rule with these objects then it's enough for me to work with. I'm just not familiar enough with these objects to know how they're built / stored or what they represent. But I'm getting there... |
Oh I see, there's a bit in the quadmesh vignette - but I agree, it's extremely hard to understand these types, that's an acquired assumption on my part - I kind of went very quickly from total confusion to near-mastery. I'll try to distill a clear description! Especially material properties, I recently learnt a lot more about those |
The |
so cool! ( I tried using the whole of etopo but the data didn't exist ) library(quadmesh)
qm <- quadmesh::quadmesh( quadmesh::etopo )
qm$vb[3, ] <- qm$vb[3, ] * 100
mapdeck() %>%
add_mesh(
data = qm
) mapdeck() %>%
add_mesh(
data = qm
, palette = "magma"
)
|
haha, I'll get you an entire coverage! |
Here is GEBCO 2019 reduced 100x, saved as compressed .rds 1.1Mb ( and zipped so I could attach it here) Attachment GEBCO_lowres.zip derived from GEBCO 2019 Grid, provided by GEBCO Compilation Group (2019) GEBCO 2019 Grid (doi:10.5285/836f016a-33be-6ddc-e053-6c86abc0788e) https://www.gebco.net/data_and_products/gridded_bathymetry_data/gebco_2019/gebco_2019_info.html |
This is cool! I've had a look at generalizing for any mesh3d object - so you can leave that to me if you like, I'll also write up how the colour properties are applied. |
hold off on the colour properties; I'm going to change it once I'll happily take a PR on the generalised |
cool cool, coming soon - I got as far as seeing the C++ is specific to quad types, so I'll push what I have and create some example mesh3d data sets |
Oh, I see it does already deal with vb/it at the C++ - I'm just not seeing anything - still exploring. |
You might be experiencing
If you open it in a browser do you get the plot? |
It does work with triangle type, so I think I'm doing something wrong when creating the mesh3d- so I'll retreat for now and make sure my examples are proper! |
Ah! Indeed, plus I was mixing up Melbourne and Hobart there .... I find with triangles it doesn't focus the view though does with quad - more than enough for me to go on now, thanks! |
Here I get some elevation data from Mapbox (no bathymetry is available), after some updates to quadmesh and reproj on github. I'm using mds-mesh3d which just now is up to date with master here. There's two comment/uncomment options for a Tas or US region, and for mesh as triangles or quads. ## remotes::install_github(c("hypertidy/ceramic", "hypertidy/quadmesh"))
## remotes::install_github("mdsumner/mapdeck@mds-mesh3d")
library(quadmesh)
library(ceramic)
library(raster)
library(mapdeck)
## choose Tas/Vic
im <- cc_elevation(extent(140, 150, -45, -36), zoom = 3); im <- im * 70
## or choose Yosemite
#im <- cc_elevation(extent(-120, -119, 37.5, 38.5), zoom = 6) * 5
tm <- triangmesh(im)
qm <- quadmesh(im)
## project mesh to longlat (choose quad or triangle mesh here)
mesh <- reproj(tm, 4326, source = projection(im))
#mesh <- reproj(qm, 4326, source = projection(im))
tok <- Sys.getenv("MAPBOX_API_KEY")
mapdeck(token = tok) %>%
add_mesh(
data = mesh
)
Context on changes in reproj/quadmesh:
(Together this gives a lot more ability to get data into mesh3d form. ) |
Thanks also for your write-up on rgl. I now see how the colours are used, and therefore probably should replicate the behaviour if a |
IMPORTANT ANNOUNCEMENT: the primitivetype property was removed completely and you should not expect it to be a part of mesh3d. r-forge/rgl@87bcf74#diff-fb1a2ca62de6cfcab71eef95a8103b38 I'll have to clean up quadmesh a fair bit to remove it, you're expected to look only for it or ib arrays for the type/s of primitives available (mesh3d can have both, and there might be other types but I'm not sure). I'll be back with more about the constructors: tmesh3d(), qmesh3d() which are clearly now the ways you are supposed to build these objects (not from templates like I always have before). |
@mdsumner do mesh objects ever have holes in them? |
Definitely! library(quadmesh)
v <- volcano
v[v > 165] <- NA
qm <- quadmesh(v)
rgl::shade3d(qm, col = "grey"); rgl::rglwidget() "But are they really holes?" I have the mighty boosh in my ear You can arbitrarily remove triangles or vertices, it doesn't matter - though you end up with a mess of course if not careful. rgl::clear3d()
qm$vb[1, qm$vb[1, ] > 30] <- NA
rgl::shade3d(qm, col = "grey"); rgl::rglwidget() |
cool, this is what I was hoping for; they are 'missing' quads/triangles, rather than 'holes' in the sense of |
That's right, I don't have CRAN code to turn sf polygons into mesh3d but hypertidy/anglr can do it
|
Are you looking for a tidy-ish encoding much like the |
Oh I see it's about SolidPolygon, sounds promising! I know you must be a bit busy rn with CRAN (plus general crazy world), but I added a library(quadmesh) ## need latest remotes::install_github("hypertidy/quadmesh")
v <- volcano
v[v > 165] <- NA
qm <- quadmesh(v)
## make it a regular mesh3d (this is the conversion world I need to flesh out)
class(qm) <- c("mesh3d", "shape3d")
qm$raster_metadata <- qm$crs <- NULL
mesh_plot(qm) ## triangles too
tm <- triangmesh(v)
## regular mesh3d
class(tm) <- c("mesh3d", "shape3d")
tm$raster_metadata <- qm$crs <- NULL
mesh_plot(tm) Created on 2020-03-19 by the reprex package (v0.3.0) |
Yes and yes. If I can get all coordinates in a column then I can use the |
I don't totally get the column thing, but is this what it needs? A SolidPolygon is the explicit path around the coordinates? Fourth column in #x <- quadmesh:::as_mesh3d_matrix(matrix(1:2, 2))
#dput(x)
x <- structure(list(vb = structure(c(0, 1, 1, 1, 1, 1, 1.5, 1, 2,
1, 2, 1, 0, 0, 1, 1, 1, 0, 1.5, 1, 2, 0, 2, 1),
.Dim = c(4L, 6L)),
ib = structure(c(1L, 2L, 5L, 4L, 2L, 3L, 6L, 5L), .Dim = c(4L,2L)),
material = list(color = c("#440154FF", "#218F8DFF", "#FDE725FF",
"#440154FF", "#218F8DFF", "#FDE725FF")),
normals = NULL, texcoords = NULL,
meshColor = "vertices"), class = c("mesh3d", "shape3d"))
## handle it or ib
id <- x$ib
xyzi <- cbind(t(x$vb[1:3, id]), rep(seq_len(ncol(id)), each = nrow(id)))
xyzi
[,1] [,2] [,3] [,4]
[1,] 0 1 1.0 1
[2,] 1 1 1.5 1
[3,] 1 0 1.5 1
[4,] 0 0 1.0 1
[5,] 1 1 1.5 2
[6,] 2 1 2.0 2
[7,] 2 0 2.0 2
[8,] 1 0 1.5 2 |
In deck.gl terminology, a And to make use of their binary data format I need to flatten the coordinates into a single vector
Which is trivial if I have a So if the mesh object can be made into a single column directly then this will save some conversion time - mesh --> df --> vector. But, it may also be useful to have a similar |
So iiuc each i-th SolidPolygon is ## i is individually a column-index of x$ib or x$it
as.vector(x$vb[1:2, x$ib[,i]]) # quad
as.vector(x$vb[1:2, x$it[,i]]) # triangle and an intermediate df with all of them is idx <- x$ib ## or x$it
data.frame(x = x$vb[1, idx], y = x$vb[2, idx], z = x$vb[3, idx], solid_polygon_id = rep(seq_len(ncol(idx)), each = nrow(idx))) To close each polygon you would first update the index with Or am I still misunderstanding? |
yes I believe your understanding is correct. |
Thoughts while they're fresh, I just used this code successfully: library(mapdeck)
mapdeck() %>% add_mesh(data = melbourne_mesh)
library(ceramic)
cc <- cc_elevation(raster::extent(147.5, 147.6, -42.1, -42.0),
zoom = 9)
mesh <- reproj::reproj(quadmesh::triangmesh(cc), 4326)
mesh$vb[3, ] <- mesh$vb[3, ] * 35
mesh$material$col <- colourvalues::color_values(mesh$vb[3, mesh$it])
mapdeck() %>% add_mesh(data = mesh)
Caveats: having troubles with raster and ceramic, so I'm dev versions of those - the code above may not work. The plan is for hypertidy/anglr to have a comprehensive
I'll build a set of mesh3d examples so we can use that as a standard, something we can fix if they are specified wrongly. The reason for this note is that mapdeck's expectations for a mesh3d, and the code I have to create them (in quadmesh) are not aligned, and not consistent with the modern mesh3d spec. So it always takes me ages to get things sorted and it's time to share an easy-example pool. |
Here's a few examples of quad and triangle mesh3d: https://github.com/hypertidy/anglr/tree/issue89/data-raw/mapdeck Don't worry about the code so much, that's in flux and lots of hypertidy-cleanout - there's a saved list of meshes, in strictly modern and proper rgl form (I hope). |
TODO
vb
andib
, triangle usesvb
andit
(?) - use thegeometry_columns
to specify these namesclose polygonsdon't need thismaterial$col
attribute onmesh3d
objectsThe text was updated successfully, but these errors were encountered: