Skip to content

Commit

Permalink
add test, docs
Browse files Browse the repository at this point in the history
  • Loading branch information
trafficonese committed Sep 2, 2024
1 parent d5d3586 commit 34facab
Show file tree
Hide file tree
Showing 7 changed files with 212 additions and 234 deletions.
8 changes: 3 additions & 5 deletions R/divicon.R
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,11 @@ diviconDependency <- function() {
#' for latitude and longitude coordinates. It allows for the application of custom HTML
#' content and CSS classes to each marker, providing high flexibility in marker design.
#'
#' @param map The Leaflet map object to which the DivIcon markers will be added.
#' @inheritParams leaflet::addAwesomeMarkers
#' @param className A single CSS class or a vector of CSS classes to apply to the DivIcon markers.
#' @param html A single HTML string or a vector of HTML strings to display within the DivIcon markers.
#' @param className A single CSS class or a vector of CSS classes.
#' @param html A single HTML string or a vector of HTML strings.
#' @param divOptions A list of extra options for Leaflet DivIcon.
#' @param options A list of extra options for the markers.
#' See \code{\link[leaflet]{markerOptions}} for more details.
#'
#' @family DivIcon Functions
#' @return The modified Leaflet map object.
#' @export
Expand Down
62 changes: 44 additions & 18 deletions R/layergroupcollision.R
Original file line number Diff line number Diff line change
Expand Up @@ -12,30 +12,60 @@ layergroupCollisionDependency <- function() {
)
}

#' Add LayerroupCollision Plugin
#' Needs data to be ordered, as frst elements will have priority.
#' @return A leaflet map object
#' Add LayerGroup Collision Plugin to Leaflet Map
#'
#' @description Integrates the LayerGroup Collision plugin into a Leaflet map,
#' which hides overlapping markers and only displays the first added marker in a
#' collision group. Markers must be static; dynamic changes, dragging, and
#' deletions are not supported.

#' The function transforms spatial data into GeoJSON format and uses `L.DivIcon`,
#' allowing you to pass HTML content and CSS classes to style the markers.
#'
#' @param group the name of the group. It needs to be single string.
#' @return A leaflet map object with the LayerGroup Collision plugin added.
#' @export
#'
#' @inheritParams leaflet::addAwesomeMarkers
#' @references \url{https://github.com/Geovation/labelgun}
#' @inheritParams addDivicon
#' @references \url{https://github.com/MazeMap/Leaflet.LayerGroup.Collision}
#'
#' @name LayerroupCollision
#' @examples
#' library(leaflet)
#' library(sf)
#' library(leaflet.extras2)
#'
#' df <- sf::st_as_sf(atlStorms2005)
#' df <- suppressWarnings(st_cast(df, "POINT"))
#' df <- df[sample(1:nrow(df), 150, replace = F),]
#' df$classes = sample(x = 1:5, nrow(df), replace = TRUE)
#'
#' leaflet() %>%
#' addProviderTiles("CartoDB.Positron") %>%
#' leaflet::addLayersControl(overlayGroups = c("Labels")) %>%
#' addLayerGroupCollision(data = df
#' , html = ~paste0(
#' '<div style="width: 70px" class="custom-html">',
#' '<div class="title">', Name, '</div>',
#' '<div class="subtitle">MaxWind: ', MaxWind, '</div>',
#' '</div>'
#' )
#' , className = ~paste0("my-label my-label-", classes)
#' , group = "Labels"
#' )
addLayerGroupCollision <- function(
map, layerId = NULL, group = NULL,
popup = NULL, popupOptions = NULL, label = NULL,
labelOptions = NULL,
map, group = NULL,
className = NULL, html = NULL,
options = markerOptions(),
margin = 5, data = getMapData(map)
) {
margin = 5, data = getMapData(map)) {

map$dependencies <- c(map$dependencies, layergroupCollisionDependency())

## Make Geojson ###########
## Make Geojson and Assign Class & HTML columns ###########
if (!inherits(data, "sf")) {
data <- sf::st_as_sf(data)
}
data$className__ <- evalFormula(className, data)
data$html__ <- evalFormula(html, data)
geojson <- yyjsonr::write_geojson_str(data)
class(geojson) <- c("geojson", "json")

Expand All @@ -45,12 +75,8 @@ addLayerGroupCollision <- function(
"addLayerGroupCollision"
)
invokeMethod(
map, data, "addLayerGroupCollision", geojson,
layerId, group, options,
className, html,
popup, popupOptions,
label, labelOptions,
margin
map, NULL, "addLayerGroupCollision",
geojson, group, margin
) %>%
expandLimits(pts$lat, pts$lng)
}
52 changes: 13 additions & 39 deletions inst/examples/layergroupcollision_app.R
Original file line number Diff line number Diff line change
Expand Up @@ -4,46 +4,29 @@ library(sf)
library(leaflet.extras2)
options("shiny.autoreload" = TRUE)

# data <- sf::st_as_sf(breweries91)
# data <- sf::st_as_sf(mapview::trails[1:100,])
# data <- st_transform(data, 4326)
# data <- st_cast(data, "POINT")
# data <- data[1:300,]

df <- sf::st_as_sf(atlStorms2005)
df <- suppressWarnings(st_cast(df, "POINT"))
df <- df[sample(1:nrow(df), 150, replace = F),]
# df$classes = sample(x = c("myclass1","myclass2","myclass3"), nrow(df), replace = TRUE)
df$classes = sample(x = 1:10, nrow(df), replace = TRUE)
df$ID <- paste0("ID_", 1:nrow(df))
df$scalerank <- sample(x = 1:10, nrow(df), replace = TRUE)
df$classes = sample(x = 1:5, nrow(df), replace = TRUE)

## Ordering is important
df <- df[order(df$scalerank, decreasing = FALSE),]
df <- df[order(df$classes, decreasing = FALSE),]

ui <- fluidPage(
## CSS-style ############
tags$head(tags$style("
.city-label {
.my-label {
background: white;
border: 1px solid #888;
position: relative;
display: inline-block;
white-space: nowrap;
}
.city-label-0 { font-size: 30px; top: -27px; }
.city-label-1 { font-size: 25px; top: -26px; }
.city-label-2 { font-size: 24px; top: -25px; }
.city-label-3 { font-size: 22px; top: -24px; }
.city-label-4 { font-size: 16px; top: -23px; }
.city-label-5 { font-size: 15px; top: -22px; }
.city-label-6 { font-size: 14px; top: -21px; }
.city-label-7 { font-size: 13px; top: -20px; }
.city-label-8 { font-size: 12px; top: -19px; }
.city-label-9 { font-size: 11px; top: -18px; }
.city-label-10{ font-size: 10px; top: -17px; }
.my-label-1 { font-size: 28px; background-color: red; top: -26px; }
.my-label-2 { font-size: 24px; background-color: orange; top: -25px; }
.my-label-3 { font-size: 22px; background-color: yellow; top: -24px; }
.my-label-4 { font-size: 16px; background-color: green; top: -23px; }
.my-label-5 { font-size: 15px; background-color: lightgreen; top: -22px; }
")),
leafletOutput("map", height = 800)
)
Expand All @@ -53,25 +36,16 @@ server <- function(input, output, session) {
output$map <- renderLeaflet({
leaflet() %>%
addProviderTiles("CartoDB.Positron") %>%
leaflet::addLayersControl(overlayGroups = c("Divicons","markers")) %>%
# addMarkers(data = df, label = ~Name,
# , group = "markers") %>%
leaflet::addLayersControl(overlayGroups = c("Labels")) %>%
addLayerGroupCollision(data = df
, html = ~paste0(
'<div class="custom-html">',
'<div class="title">', Name, '</div>',
'<div class="subtitle">MaxWind: ', MaxWind, '</div>',
'<div class="title">', Name, '</div>',
'<div class="subtitle">MaxWind: ', MaxWind, '</div>',
'</div>'
)
, className = ~paste0("city-label city-label-", classes)
, label = ~Name
, layerId = ~ID
, group = "Divicons"
, popup = ~paste("ID: ", ID, "<br>",
"Name: ", Name, "<br>",
"MaxWind:", MaxWind, "<br>",
"MinPress:", MinPress)
, options = markerOptions(draggable = TRUE)
, className = ~paste0("my-label my-label-", classes)
, group = "Labels"
)

})
Expand Down
143 changes: 16 additions & 127 deletions inst/htmlwidgets/lfx-layergroupcollision/layergroup-binding.js
Original file line number Diff line number Diff line change
@@ -1,139 +1,28 @@
/* global LeafletWidget, $, L, Shiny, HTMLWidgets */
LeafletWidget.methods.addLayerGroupCollision = function(
data, layerId, group, options,
className, html, popup, popupOptions,
label, labelOptions, margin) {

data, group, margin) {

var collisionLayer = L.LayerGroup.collision({margin: margin});
console.log("collisionLayer"); console.log(collisionLayer)

// Manually parse the GeoJSON and create the L.Markers one by one
// Note that 'cities' is defined in the natural earth data files.
console.log("data"); console.log(data)
for (var i=0; i < data.features.length; i++) {

var feat = data.features[i];
var labelClass = 'city-label city-label-' + feat.properties.strklasse_numeric;

// Note that the markers are not interactive because MSIE on a WinPhone will take *ages*
// to run addEventListener() on them.
var marker = L.marker(L.GeoJSON.coordsToLatLng(feat.geometry.coordinates), {
icon: L.divIcon({
html:
"<span class='" + labelClass + "'>" +
feat.properties.strnummer +
"</span>"
})
,interactive: false // Post-0.7.3
,clickable: false // 0.7.3
});
// Note that the markers are not interactive because MSIE on a WinPhone will take *ages*
// to run addEventListener() on them.
var marker = new L.marker(
L.GeoJSON.coordsToLatLng(feat.geometry.coordinates), {
icon: L.divIcon({
html:
"<span class='" + feat.properties["className__"] + "'>" +
feat.properties["html__"] +
"</span>"
})
, interactive: false // Post-0.7.3
, clickable: false // 0.7.3
});

collisionLayer.addLayer(marker);
}
collisionLayer.addTo(this);
this.layerManager.addLayer(collisionLayer, "collison", null, group);

};
/*
(function() {
var collisionLayer = L.LayerGroup.collision({margin:5});
// Make a Dataframe
let df = new LeafletWidget.DataFrame()
.col("lat", lat)
.col("lng", lng)
.col("layerId", layerId)
.col("group", group)
.col("popup", popup)
.col("popupOptions", popupOptions)
.col("label", label)
.col("labelOptions", labelOptions)
.col("className", className)
.col("html", html)
.cbind(options)
.cbind(crosstalkOptions || {});
// Add Cluster
let clusterGroup = this.layerManager.getLayer("cluster", clusterId),
cluster = clusterOptions !== null;
if (cluster && !clusterGroup) {
clusterGroup = L.markerClusterGroup.layerSupport(clusterOptions);
if(clusterOptions.freezeAtZoom) {
let freezeAtZoom = clusterOptions.freezeAtZoom;
delete clusterOptions.freezeAtZoom;
clusterGroup.freezeAtZoom(freezeAtZoom);
}
clusterGroup.clusterLayerStore = new LeafletWidget.ClusterLayerStore(clusterGroup);
}
let extraInfo = cluster ? { clusterId: clusterId } : {};
for (let i = 0; i < df.nrow(); i++) {
if($.isNumeric(df.get(i, "lat")) && $.isNumeric(df.get(i, "lng"))) {
(function() {
let thisId = df.get(i, "layerId");
let thisGroup = cluster ? null : df.get(i, "group");
// Create a new marker with DivIcon
var divIconOptions = Object.assign({}, divOptions, {
className: df.get(i, "className"),
html: df.get(i, "html")
});
var divmarker = new L.Marker(
[df.get(i, "lat"), df.get(i, "lng")],
Object.assign({}, options, {
icon: new L.DivIcon(divIconOptions)
}));
collisionLayer.addLayer(divmarker);
if (cluster) {
clusterGroup.clusterLayerStore.add(divmarker, thisId);
} else {
this.layerManager.addLayer(divmarker, "marker", thisId, thisGroup, df.get(i, "ctGroup", true), df.get(i, "ctKey", true));
}
// Bind popup to the marker if popup content is provided
let popup = df.get(i, "popup");
let popupOptions = df.get(i, "popupOptions");
if (popup !== null) {
if (popupOptions !== null){
divmarker.bindPopup(popup, popupOptions);
} else {
divmarker.bindPopup(popup);
}
}
// Assign label (tooltip) to marker if label content is provided
let label = df.get(i, "label");
let labelOptions = df.get(i, "labelOptions");
if (label !== null) {
if (labelOptions !== null) {
if(labelOptions.permanent) {
divmarker.bindTooltip(label, labelOptions).openTooltip();
} else {
divmarker.bindTooltip(label, labelOptions);
}
} else {
divmarker.bindTooltip(label);
}
}
// Add the marker to the map's layer manager
this.layerManager.addLayer(divmarker, "marker", thisId, thisGroup);
// Add Listener
divmarker.on("click", LeafletWidget.methods.mouseHandler(this.id, thisId, thisGroup, "marker_click", extraInfo), this);
divmarker.on("mouseover", LeafletWidget.methods.mouseHandler(this.id, thisId, thisGroup, "marker_mouseover", extraInfo), this);
divmarker.on("mouseout", LeafletWidget.methods.mouseHandler(this.id, thisId, thisGroup, "marker_mouseout", extraInfo), this);
divmarker.on("dragend", LeafletWidget.methods.mouseHandler(this.id, thisId, thisGroup, "marker_dragend", extraInfo), this);
}).call(this);
}
}
if (cluster) {
this.layerManager.addLayer(clusterGroup, "cluster", clusterId, group);
}
collisionLayer.addTo(this);
}).call(this);
*/
Loading

0 comments on commit 34facab

Please sign in to comment.