Comments (20)
Somehow related to this, a very useful feature could also to be able to edit the attribute table of a vector when editing it using "editFeature".
For example: consider I want to "split" an existing polygon into multiple polygons, thus creating new geometries, and that I have an "ID" column in the original vector's attributes table. It would be nice to be able to interactively assing a new "ID" to the newly created polygons before returning the updated object to "R".
I recon this may be complex, and maybe calls for a dedicated shiny/lealflet app, but I think it's a "use case" worth considering.
from mapedit.
Agreed! A very nice idea. I usually approach spatial data from the geometry side of things, not from he attribute side, so this perspective never occurred to me. But I do find this a very interesting option that I would love to see implemented.
I also agree that this aligns quite nicely with the objective of feature editing. Here's a first few points that pop into my head that (I think) need consideration:
-
do we require the user to have a sf object for this? I.e. will it be up to the user to provide some empty but valid sf geometry column? I would prefer to allow all sorts of tabular data.
(kr) I think any tabular input as a starting point and would like for a user to not have to specify an empty geometry column. If input issf
could default to the current geometry column.
(ta) Agreed, this will also align with the RConsortium suggestion to try to be as general as possible. -
If we allow simple tabular data, how to go about rows with no added/drawn geometries?
st_point(c(NA, NA))
as a default?
(kr) You will know better here the best way to represent a null geometry insf
. What other options do we have?
(ta) I thinkst_point(c(NA, NA))
is the easiest for mixed geometries. It would be nice to scan the table for uniqueness. E.g. in case there are onlyPOLYGON
features, we should also provide emptyPOLYGON
s for the rows without added geometries. -
I think we need to have methods to convert drawn leaflet geojson objects to sf class
sfg
/XY
as inst_point(c(1, 2))
which comprise the set of simplest geometry classes in sf (e.g.POINT
,LINESTRING
,POLYGON
and all theMULTI*
variants +GEOMETRYCOLLECTION
). Which, however could also help to structure the internals of mapedit more concisely.
(kr) not sure I understand. Do you mean everything should be converted tosf
?
(ta) maybe it is I who doesn't understand. Let me wait for the poc... -
can we come up with a general way of providing a
editAttributes
method that works for both plain table data as well as sf class data? Maybe some sort ofedit attributes
button ineditMap
/editFeatures
to switch to editing-attributes-mode? -
how do we layout? I.e. can we come up with a useful pane viewer layout to accommodate enough space for both map and table?
(kr) will be tricky, since ui will hide much of the map. I wonder if the ui should exist to the side of the map.
As I said, these just popped into my head when thinking about this a little.
In any case, a draft poc would be fabulous to have some solid example case to progress from.
I very much like this idea, especially as it is something that standard GIS systems usually don't provide in an easy fashion (if at all),
from mapedit.
Thanks @tiernanmartin and @tim-salabim. See if this rough poc is headed in the right direction, but I just realized in this implementation if you draw first (likely) then successive edit and/or delete will be ignored. Will make the quick fix if I get positive feedback. Also, in this case, we will most likely want to turn off multiple mode in Leaflet.draw
.
library(leaflet)
library(mapview)
library(mapedit)
library(sf)
library(DT)
library(shiny)
make_an_sf <- function(dat) {
ui <- fluidPage(
fluidRow(
column(6,DT::dataTableOutput("tbl",width="100%", height="400px")),
column(6,editModUI("map"))
),
fluidRow(actionButton("donebtn", "Done"))
)
server <- function(input, output, session) {
data_copy <- st_as_sf(
dat,
geometry = st_sfc(lapply(seq_len(nrow(dat)),function(i){st_point()}))
)
edits <- callModule(
editMod,
leafmap = mapview()@map,
id = "map"
)
output$tbl <- DT::renderDataTable({
DT::datatable(
dat,
options = list(scrollY="400px"),
# could support multi but do single for now
selection = "single"
)
})
# unfortunately I did not implement last functionality
# for editMap, so do it the hard way
# last seems useful, so I might circle back and add that
EVT_DRAW <- "map_draw_new_feature"
EVT_EDIT <- "map_draw_edited_features"
EVT_DELETE <- "map_draw_deleted_features"
nsm <- function(event="", id="map") {
paste0(session$ns(id), "-", event)
}
observe({
possible <- list(
draw = input[[nsm(EVT_DRAW)]],
edit = input[[nsm(EVT_EDIT)]],
delete = input[[nsm(EVT_DELETE)]]
)
# get last event
last <- Filter(Negate(is.null), possible)
# really dislike that we need to do in R
pos <- Position(Negate(is.null), possible)
# get selected row
selected <- isolate(input$tbl_rows_selected)
skip = FALSE
# ignore if selected is null
# not great but good enough for poc
if(is.null(selected)) {skip = TRUE}
# ignore if no event
if(length(last) == 0) {skip = TRUE}
# replace if draw or edit
if(skip==FALSE && (names(possible)[pos] %in% c("edit","draw"))) {
sf::st_geometry(data_copy[selected,]) <<- sf::st_geometry(
mapedit:::st_as_sfc.geo_list(unname(last)[[1]])
)
}
# remove if delete
if(skip==FALSE && (names(possible)[pos] %in% c("delete"))) {
sf::st_geometry(data_copy[selected,]) <<- sf::st_sfc(st_point())
}
})
# provide mechanism to return after all done
observeEvent(input$donebtn, {
# convert to sf
stopApp(st_sf(data_copy,crs=4326))
})
}
return(runApp(shinyApp(ui,server)))
}
# let's act like breweries does not have geometries
brewsub <- breweries[,1:4,drop=TRUE]
brewpub <- make_an_sf(brewsub)
mapview(brewpub)
from mapedit.
@tiernanmartin oh good! I did not test with MULTI
so not surpised if not working. I will play a little to see if I can get it to work, but the issue here is Leaflet.draw
does not accumulate draw events, so each completed feature will be sent. To achieve multi, we would need to add some other UI to know when grouping should occur. edit and delete though are accumulated and sent as collection. I will also make change so edit and delete will work.
from mapedit.
Rather than copy/pasting with each iteration, I saved the code here so we can track changes. I was able to add zoom on datatable selected row.
Also, I think I have an idea for MULTI
, but I need to do some testing.
@tim-salabim, I think lessons learned in this exercise will be very useful, but I am not sure how far we shoudl push this poc. Zoom/highlight will likely be necessary for edit attributes functionality.
from mapedit.
@timelyportfolio this is coming along nicely I think! The zoom doesn't seem to work for points as, I guess, map.fitBounds(map._layers[layerid].getBounds());
doesn't find any bounds for 0 dimensional data.
Additionally, I am wondering whether a vertically split layout with 2 3rds map and 1 3rd DT would be better? Given that we work one row at a time in this setup, I feel that the table doesn't need so much space.
from mapedit.
I am coming to this discussion a bit late, but it is exactly the workflow I have been looking for.
Lately I have also been integrating editable DT
(rstudio/DT#480) in some of my other shiny apps. I think integrating an editable DT
with the app example does make the attributes editable.
I have created a pull request (fb2f63a) for the example but basically you just create a dataTableProxy
take the value edits and apply them to the underlying data.frame.
# update table with entered notes
proxy = dataTableProxy('tbl')
observeEvent(input$tbl_cell_edit, {
info = input$tbl_cell_edit
str(info)
i = info$row
j = info$col
v = info$value
info$value <- as.character(info$value)
data_copy[i, j] <<- DT::coerceValue(v, data_copy[i, j])
replaceData(proxy, data_copy, resetPaging = FALSE) # important
})
@lbusett and @timelyportfolio I am curious if this is the functionality you were after and if this experiment had progressed any further?
from mapedit.
This is a very interesting idea, and I very much appreciate the suggestion. It seems like this functionality might align nicely with our yet untouched objective of attribute editing and offer some synergy with existing mapedit capabilities. I will try to assemble a quick poc.
@tim-salabim, would love to hear your thoughts.
from mapedit.
@tim-salabim, I commented inline above. Thanks for listing out your thoughts. I will try to create the quickest poc I can to stimulate additional thought. http://geojson.io will be a good reference point and also
from mapedit.
Also added some comments inline above
from mapedit.
One other idea would be we could use selectMap
to join existing features to a data.frame. So select feature and select row to join. Return object would be sf
with joined data and features.
from mapedit.
@timelyportfolio It's hard for me to imagine a more perfect poc - your gif illustrates the exact workflow I had in mind! Really nice stuff 👍
It seems that this implementation doesn't allow users to create MULTI*
/GEOMETRY COLLECTION
geometries - is that correct?
from mapedit.
actually I am wrong. In multiple mode, the feature is returned as a collection, so might be relatively easy I think to add multi to draw. actually wrong each draw is separate
from mapedit.
Ok, this allows draw, edit, delete, but I don't know a good way to easily handle multiple edit, deletes. It can be done but requires much more complicated logic. Also need to add zoom_to functionality on datatable select if a feature exists on the selected row, but that also requires more work :).
library(leaflet)
library(mapview)
library(mapedit)
library(sf)
library(DT)
library(shiny)
make_an_sf <- function(dat) {
ui <- fluidPage(
fluidRow(
column(6,DT::dataTableOutput("tbl",width="100%", height="400px")),
column(6,editModUI("map"))
),
fluidRow(actionButton("donebtn", "Done"))
)
server <- function(input, output, session) {
data_copy <- st_as_sf(
dat,
geometry = st_sfc(lapply(seq_len(nrow(dat)),function(i){st_point()}))
)
edits <- callModule(
editMod,
leafmap = mapview()@map,
id = "map"
)
output$tbl <- DT::renderDataTable({
DT::datatable(
dat,
options = list(scrollY="400px"),
# could support multi but do single for now
selection = "single"
)
})
# unfortunately I did not implement last functionality
# for editMap, so do it the hard way
# last seems useful, so I might circle back and add that
EVT_DRAW <- "map_draw_new_feature"
EVT_EDIT <- "map_draw_edited_features"
EVT_DELETE <- "map_draw_deleted_features"
nsm <- function(event="", id="map") {
paste0(session$ns(id), "-", event)
}
addObserve <- function(event) {
observeEvent(
input[[nsm(event)]],
{
evt <- input[[nsm(event)]]
# for now if edit, just consider, first feature
# of the FeatureCollection
if(event == EVT_EDIT) {
evt <- evt$features[[1]]
}
# get selected row
selected <- isolate(input$tbl_rows_selected)
skip = FALSE
# ignore if selected is null
# not great but good enough for poc
if(is.null(selected)) {skip = TRUE}
# ignore if no event
#if(length(evt) == 0) {skip = TRUE}
print(evt)
# replace if draw or edit
if(skip==FALSE) {
sf::st_geometry(data_copy[selected,]) <<- sf::st_geometry(
mapedit:::st_as_sfc.geo_list(evt)
)
}
})
}
addObserve(EVT_DRAW)
addObserve(EVT_EDIT)
observeEvent(
input[[nsm(EVT_DELETE)]],
{
evt <- input[[nsm(EVT_DELETE)]]
# get selected row
selected <- isolate(input$tbl_rows_selected)
skip = FALSE
# ignore if selected is null
# not great but good enough for poc
if(is.null(selected)) {skip = TRUE}
# ignore if no event
#if(length(last) == 0) {skip = TRUE}
# remove if delete
if(skip==FALSE) {
sf::st_geometry(data_copy[selected,]) <<- st_geometry(sf::st_sfc(st_point()))
}
}
)
# provide mechanism to return after all done
observeEvent(input$donebtn, {
# convert to sf
stopApp(st_sf(data_copy,crs=4326))
})
}
return(runApp(shinyApp(ui,server)))
}
# let's act like breweries does not have geometries
brewsub <- breweries[,1:4,drop=TRUE]
brewpub <- make_an_sf(brewsub)
mapview(brewpub)
from mapedit.
ideally, zoom_to + highlight
from mapedit.
Wow, this is awesome.
from mapedit.
Hi. I just wish to add that this would be great functionality for the remote sensing community also. For example, it would allow selecting Areas of Interest over a satellite image on zones with different characteristics (e.g., different land use), to be used for example for plotting purposes (e.g., plotting time series), or as training / testing data for raster classification. These are common tasks, which are currently accomplished using QGIS, ENVI, or other RS software.
Any plans to develop it further? (sorry I can't help but I am not fluent (yet) with leaflet/shiny).
from mapedit.
@lbusett good point! This will be developed further, no doubt. We are currently not pushing mapedit development too much as we would like to wait until leaflet has been upgraded to use leafletjs 1.x (there's a lot of changes and a lot of new goodies). Nonetheless, I think it would be useful to have a working basic implementation of this functionality for users to test and report back so we can get a feel of what the expectations for such a tool are in real life.
Also note, that direct querying of raster data will likely not happen until package stars has been developed further so that we can query using sf (which is the infrastructure used by mapedit). Until then you will be restricted to use the raster imagery as a background map and do the extract
yourself after digitizing.
from mapedit.
Thanks for the reply. Looking forward for this!
PS: I'm aware of the issue about not being able to "query" the raster data. Indeed, I recently developed some routines for "efficient" raster extraction starting from sf
objects within a "spatial processing wrappers package" I'm currently developing.
Though the package is not yet publicly released - and is intended mostly as a "facilitator" for R-based analysis within my group, thus often replicating functionality of other packages with just a modified syntax - , maybe they can be of interest (at least until "stars" comes around):
https://lbusett.github.io/sprawl/reference/extract_rast.html
https://lbusett.github.io/sprawl/articles/articles/extract_rast_example.html
from mapedit.
@lbusett, thanks, and yes we plan to add attribute editing soon. As of now, we are waiting on Leaflet > 1. Hopefully, we will be back to work on this soon.
from mapedit.
Related Issues (20)
- Load back previously drawn polygons to mapedit map
- editor Module in demo example fails in mapedit 0.6.0 but works in 0.5.0 HOT 2
- selectFeatures draws on world map even if crs = NA (for mode = "click" only) HOT 7
- New feature to download edited data HOT 2
- Click events for leafpm editToolbar buttons HOT 5
- leafpm removes markers added using leafletProxy HOT 2
- Use raster in base map with editMap HOT 2
- Issues installing r-spatial packages HOT 1
- is there a way to display only some of the tools HOT 2
- Is this package still actively developed? HOT 1
- Strange case of difficult viewing HOT 7
- Integration leafmap new feature HOT 17
- 'initBaseMaps' and 'mode = "click"' for selectFeatures
- Warning: `select_()` was deprecated in dplyr 0.7.0. HOT 5
- selectFeatures error possibly related to "draw" mode HOT 3
- mapedit installation on Google colab
- Error triggered by "Clear All" when there are three or more drawn objects.
- Coordinates using editMap() with addRasterRGB() are wrong HOT 3
- mosaic_view() running problem HOT 3
- callModule(editMod, ...) only returns the geometry
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from mapedit.