Skip to content

Commit 740096b

Browse files
Merge pull request #94 from r-spatial/leafpm
Add leafpm
2 parents d564265 + 984cbc3 commit 740096b

File tree

12 files changed

+152
-36
lines changed

12 files changed

+152
-36
lines changed

DESCRIPTION

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,12 @@ Package: mapedit
22
Title: Interactive Editing of Spatial Data in R
33
Description: Suite of interactive functions and helpers for selecting and editing
44
geospatial data.
5-
Version: 0.4.3
6-
Date: 2018-08-16
5+
Version: 0.5.0
6+
Date: 2019-03-16
77
Authors@R: c(
88
person("Tim", "Appelhans", role = c("aut", "cre"), email = "[email protected]"),
9-
person("Kenton", "Russell", role = c("aut"))
9+
person("Kenton", "Russell", role = c("aut")),
10+
person("Lorenzo", "Busetto", role = c("aut"))
1011
)
1112
URL: https://github.com/r-spatial/mapedit
1213
BugReports: https://github.com/r-spatial/mapedit/issues
@@ -20,6 +21,7 @@ Imports:
2021
jsonlite,
2122
leaflet (>= 2.0.1),
2223
leaflet.extras (>= 1.0),
24+
leafpm,
2325
mapview,
2426
miniUI,
2527
sf (>= 0.5-2),
@@ -30,4 +32,4 @@ Enhances:
3032
geojsonio
3133
Encoding: UTF-8
3234
LazyData: true
33-
RoxygenNote: 6.1.0
35+
RoxygenNote: 6.1.1

NEWS.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
## mapedit 0.5.0
2+
3+
### New Features
4+
5+
* add `editor = "leafpm"` to `draw*()` and `edit*()` functions to use the `Leaflet.pm` pluging for editing. `Leaflet.pm` provides support for creating and editing holes, snapping, and integrates better with some `multi*` features. Note, `mapedit` now offers two editors `"leaflet.extras"` and `"leafpm"`, since each have advantages and disadvantages.
6+
7+
18
## mapedit 0.4.3
29

310
### New Features

R/draw.R

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#' Firefox is an exception. See Details for instructions on how to enable this
1616
#' behaviour in Firefox.
1717
#' @param title \code{string} to customize the title of the UI window.
18+
#' @param editor \code{character} either "leaflet.extras" or "leafpm"
1819
#' @param ... additional arguments passed on to \code{\link{editMap}}.
1920
#'
2021
#' @details
@@ -33,12 +34,14 @@ drawFeatures = function(map = NULL,
3334
record = FALSE,
3435
viewer = shiny::paneViewer(),
3536
title = "Draw Features",
37+
editor = c("leaflet.extras", "leafpm"),
3638
...) {
3739
res = editMap(x = map,
3840
sf = sf,
3941
record = record,
4042
viewer = viewer,
4143
title = title,
44+
editor = editor,
4245
...)
4346
if (!inherits(res, "sf") && is.list(res)) res = res$finished
4447
return(res)

R/edit.R

Lines changed: 49 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ editMap <- function(x, ...) {
3434
#' @param crs see \code{\link[sf]{st_crs}}.
3535
#' @param title \code{string} to customize the title of the UI window. The default
3636
#' is "Edit Map".
37+
#' @param editor \code{character} either "leaflet.extras" or "leafpm"
3738
#'
3839
#' @details
3940
#' When setting \code{viewer = browserViewer(browser = getOption("browser"))} and
@@ -51,6 +52,7 @@ editMap.leaflet <- function(
5152
ns = "mapedit-edit", record = FALSE, viewer = shiny::paneViewer(),
5253
crs = 4326,
5354
title = "Edit Map",
55+
editor = c("leaflet.extras", "leafpm"),
5456
...
5557
) {
5658
stopifnot(!is.null(x), inherits(x, "leaflet"))
@@ -72,7 +74,7 @@ editMap.leaflet <- function(
7274
right = miniUI::miniTitleBarButton("done", "Done", primary = TRUE)
7375
),
7476
tags$script(HTML(
75-
"
77+
"
7678
// close browser window on session end
7779
$(document).on('shiny:disconnected', function() {
7880
// check to make sure that button was pressed
@@ -96,7 +98,8 @@ $(document).on('shiny:disconnected', function() {
9698
targetLayerId = targetLayerId,
9799
sf = sf,
98100
record = record,
99-
crs = crs
101+
crs = crs,
102+
editor = editor
100103
)
101104

102105
observe({crud()})
@@ -136,20 +139,22 @@ editMap.mapview <- function(
136139
ns = "mapedit-edit", record = FALSE, viewer = shiny::paneViewer(),
137140
crs = 4326,
138141
title = "Edit Map",
142+
editor = c("leaflet.extras", "leafpm"),
139143
...
140144
) {
141145
stopifnot(!is.null(x), inherits(x, "mapview"), inherits(x@map, "leaflet"))
142146

143147
editMap.leaflet(
144148
x@map, targetLayerId = targetLayerId, sf = sf,
145149
ns = ns, viewer = viewer, record = TRUE, crs = crs,
146-
title = title
150+
title = title,
151+
editor = editor
147152
)
148153
}
149154

150155
#' @name editMap
151156
#' @export
152-
editMap.NULL = function(x, ...) {
157+
editMap.NULL = function(x, editor = c("leaflet.extras", "leafpm"), ...) {
153158
m = mapview::mapview()@map
154159
m = leaflet::fitBounds(
155160
m,
@@ -158,7 +163,7 @@ editMap.NULL = function(x, ...) {
158163
lng2 = 180, #as.numeric(sf::st_bbox(x)[3]),
159164
lat2 = 90 #as.numeric(sf::st_bbox(x)[4])
160165
)
161-
ed = editMap(m, record=TRUE)
166+
ed = editMap(m, record = TRUE, editor = editor)
162167
ed_record <- ed$finished
163168
attr(ed_record, "recorder") <- attr(ed, "recorder", exact = TRUE)
164169
ed_record
@@ -196,6 +201,7 @@ editFeatures = function(x, ...) {
196201
#' @param crs see \code{\link[sf]{st_crs}}.
197202
#' @param title \code{string} to customize the title of the UI window. The default
198203
#' is "Edit Map".
204+
#' @param editor \code{character} either "leaflet.extras" or "leafpm"
199205
#'
200206
#' @details
201207
#' When setting \code{viewer = browserViewer(browser = getOption("browser"))} and
@@ -217,9 +223,19 @@ editFeatures.sf = function(
217223
crs = 4326,
218224
label = NULL,
219225
title = "Edit Map",
226+
editor = c("leaflet.extras", "leafpm"),
220227
...
221228
) {
222229

230+
# store original projection of edited object ----
231+
orig_proj <- sf::st_crs(x)
232+
if (is.na(orig_proj)) {
233+
stop("The CRS of the input object is not set. Aborting. `mapedit` does not currently
234+
allow editing objects with arbitrary coordinates system. Please set the
235+
CRS of the input using `sf::st_set_crs()` (for `sf` objects) or `proj4string()
236+
for `sp` objects", call. = FALSE)
237+
}
238+
223239
x$edit_id = as.character(1:nrow(x))
224240

225241
if (is.null(map)) {
@@ -252,10 +268,27 @@ editFeatures.sf = function(
252268
)
253269
}
254270

271+
# currently we don't have a way to set custom options for leaflet.pm
272+
# and we will want to customize allowSelfIntersection depending on feature types
273+
if(inherits(map, "mapview")) map = map@map
274+
if(editor[1] == "leafpm") {
275+
# now let's see if any of the features are polygons
276+
if(any(sf::st_dimension(x) == 2)) {
277+
map = leafpm::addPmToolbar(
278+
map,
279+
targetGroup = "toedit",
280+
toolbarOptions = leafpm::pmToolbarOptions(drawCircle = FALSE),
281+
drawOptions = leafpm::pmDrawOptions(allowSelfIntersection = FALSE),
282+
editOptions = leafpm::pmEditOptions(allowSelfIntersection = FALSE),
283+
cutOptions = leafpm::pmCutOptions(allowSelfIntersection = FALSE)
284+
)
285+
}
286+
}
287+
255288
crud = editMap(
256289
map, targetLayerId = "toedit",
257290
viewer = viewer, record = record,
258-
crs = crs, title = title, ...
291+
crs = crs, title = title, editor = editor, ...
259292
)
260293

261294
merged <- Reduce(
@@ -293,6 +326,16 @@ editFeatures.sf = function(
293326

294327
merged <- dplyr::select_(merged, "-edit_id")
295328

329+
# re-transform to original projection if needed ----
330+
if (sf::st_crs(merged) != orig_proj) {
331+
merged <- sf::st_transform(merged, orig_proj)
332+
}
333+
334+
# warn if anything is not valid
335+
if(!all(sf::st_is_valid(merged))) {
336+
warning("returned features do not appear valid; please inspect closely", call. = FALSE)
337+
}
338+
296339
# return merged features
297340
if(record==TRUE) {
298341
attr(merged, "recorder") <- attr(crud, "recorder", exact=TRUE)

R/merge.R

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,17 +33,30 @@ merge_edit <- function(
3333

3434
orig_ids = orig2[[names(by)[1]]]
3535

36-
edit_ids = edits[,by[[1]], drop=TRUE]
36+
edit_ids = edits[, by[[1]], drop=TRUE]
3737

3838
mapply(
3939
function(ed, ed_id) {
4040
matched_id_row = which(orig_ids == ed_id)
41-
sf::st_geometry(orig2)[matched_id_row] <<- sf::st_geometry(sf::st_cast(
42-
sf::st_sfc(ed),
43-
as.character(sf::st_geometry_type(
44-
sf::st_geometry(orig2[matched_id_row,])
45-
))
41+
42+
# get type of original
43+
orig_type <- as.character(sf::st_geometry_type(
44+
sf::st_geometry(orig[matched_id_row,])
4645
))
46+
47+
tryCatch(
48+
sf::st_geometry(orig2)[matched_id_row] <<- sf::st_geometry(sf::st_cast(
49+
sf::st_sfc(ed),
50+
orig_type
51+
)),
52+
error = function(e) {
53+
sf::st_geometry(orig2)[matched_id_row] <<- ed
54+
warning(
55+
paste0("Unable to cast back to original type - ", e$message, " - but this is often caused by intermediate step."),
56+
call. = FALSE
57+
)
58+
}
59+
)
4760
return(NULL)
4861
},
4962
sf::st_geometry(edits),
@@ -67,6 +80,7 @@ merge_edit <- function(
6780
#))
6881

6982
#sf::st_geometry(orig2)[matched_id_rows] <- sf::st_geometry(edits)
83+
7084
orig2
7185
}
7286

R/modules.R

Lines changed: 37 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ editModUI <- function(id, ...) {
9898
#' \code{GeoJSON}.
9999
#' @param record \code{logical} to record all edits for future playback.
100100
#' @param crs see \code{\link[sf]{st_crs}}.
101+
#' @param editor \code{character} either "leaflet.extras" or "leafpm"
101102
#'
102103
#' @return server function for Shiny module
103104
#' @import shiny
@@ -108,29 +109,40 @@ editMod <- function(
108109
targetLayerId = NULL,
109110
sf = TRUE,
110111
record = FALSE,
111-
crs = 4326
112+
crs = 4326,
113+
editor = c("leaflet.extras", "leafpm")
112114
) {
113115
# check to see if addDrawToolbar has been already added to the map
114116
if(is.null(
115117
Find(
116118
function(cl) {
117-
cl$method == "addDrawToolbar"
119+
cl$method == "addDrawToolbar" || cl$method == "addPmToolbar"
118120
},
119121
leafmap$x$calls
120122
)
121123
)) {
122-
# add draw toolbar if not found
123-
leafmap <- leaflet.extras::addDrawToolbar(
124-
leafmap,
125-
targetGroup = targetLayerId,
126-
polylineOptions = leaflet.extras::drawPolylineOptions(repeatMode = TRUE),
127-
polygonOptions = leaflet.extras::drawPolygonOptions(repeatMode = TRUE),
128-
circleOptions = FALSE,
129-
rectangleOptions = leaflet.extras::drawRectangleOptions(repeatMode = TRUE),
130-
markerOptions = leaflet.extras::drawMarkerOptions(repeatMode = TRUE),
131-
circleMarkerOptions = leaflet.extras::drawCircleMarkerOptions(repeatMode = TRUE),
132-
editOptions = leaflet.extras::editToolbarOptions()
133-
)
124+
if(editor[1] == "leaflet.extras") {
125+
# add draw toolbar if not found
126+
leafmap <- leaflet.extras::addDrawToolbar(
127+
leafmap,
128+
targetGroup = targetLayerId,
129+
polylineOptions = leaflet.extras::drawPolylineOptions(repeatMode = TRUE),
130+
polygonOptions = leaflet.extras::drawPolygonOptions(repeatMode = TRUE),
131+
circleOptions = FALSE,
132+
rectangleOptions = leaflet.extras::drawRectangleOptions(repeatMode = TRUE),
133+
markerOptions = leaflet.extras::drawMarkerOptions(repeatMode = TRUE),
134+
circleMarkerOptions = leaflet.extras::drawCircleMarkerOptions(repeatMode = TRUE),
135+
editOptions = leaflet.extras::editToolbarOptions()
136+
)
137+
}
138+
139+
if(editor[1] == "leafpm") {
140+
leafmap <- leafpm::addPmToolbar(
141+
leafmap,
142+
targetGroup = targetLayerId,
143+
toolbarOptions = leafpm::pmToolbarOptions(drawCircle = FALSE)
144+
)
145+
}
134146
}
135147

136148
output$map <- leaflet::renderLeaflet({leafmap})
@@ -174,9 +186,20 @@ editMod <- function(
174186

175187
shiny::observeEvent(input[[EVT_DELETE]], {
176188
deleted <- input[[EVT_DELETE]]
189+
177190
# find the deleted features and update finished
178191
# start by getting the leaflet ids to do the match
179192
ids <- unlist(lapply(featurelist$finished, function(x){x$properties$`_leaflet_id`}))
193+
194+
# leaflet.pm returns only a single feature while leaflet.extras returns feature collection
195+
# convert leaflet.pm so logic will be the same
196+
if(editor == "leafpm") {
197+
deleted <- list(
198+
type = "FeatureCollection",
199+
features = list(deleted)
200+
)
201+
}
202+
180203
# now modify finished to match edited
181204
lapply(deleted$features, function(x) {
182205
loc <- match(x$properties$`_leaflet_id`, ids)

inst/examples/examples_leafpm.R

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
library(sf)
2+
library(mapview)
3+
4+
#devtools::install_github("r-spatial/mapedit@leafpm")
5+
library(mapedit)
6+
#devtools::install_github("r-spatial/leafpm")
7+
library(leafpm)
8+
9+
10+
editFeatures(franconia[1:3,], editor = "leafpm")

man/drawFeatures.Rd

Lines changed: 4 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

man/editFeatures.Rd

Lines changed: 4 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)