Skip to content

plotly crosstalk experiments #346

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

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion DESCRIPTION
Original file line number Diff line number Diff line change
Expand Up @@ -64,5 +64,5 @@ Suggests:
RJSONIO,
purrr,
testthat
RoxygenNote: 5.0.1
RoxygenNote: 6.0.1
LazyData: true
2 changes: 1 addition & 1 deletion NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@

S3method("[",leaflet_awesome_icon_set)
S3method("[",leaflet_icon_set)
S3method(metaData,SharedData)
S3method(metaData,SpatialLinesDataFrame)
S3method(metaData,SpatialPointsDataFrame)
S3method(metaData,SpatialPolygonsDataFrame)
S3method(metaData,data.frame)
S3method(metaData,list)
S3method(metaData,map)
S3method(metaData,sf)
S3method(metaData,sharedData)
S3method(pointData,POINT)
S3method(pointData,SharedData)
S3method(pointData,SpatialPoints)
Expand Down
7 changes: 5 additions & 2 deletions R/layers.R
Original file line number Diff line number Diff line change
Expand Up @@ -1121,8 +1121,11 @@ addPolygons <- function(
dashArray = dashArray, smoothFactor = smoothFactor, noClip = noClip
))
pgons = derivePolygons(data, lng, lat, missing(lng), missing(lat), "addPolygons")
invokeMethod(map, data, 'addPolygons', pgons, layerId, group, options, popup, popupOptions, safeLabel(label, data), labelOptions, highlightOptions) %>%
expandLimitsBbox(pgons)
invokeMethod(
map, data, 'addPolygons', pgons, layerId, group, options, popup,
popupOptions, safeLabel(label, data), labelOptions, highlightOptions,
getCrosstalkOptions(data)
) %>% expandLimitsBbox(pgons)
}

#' @rdname remove
Expand Down
2 changes: 1 addition & 1 deletion R/normalize-SharedData.R
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#' @export
metaData.sharedData <- function(obj) {
metaData.SharedData <- function(obj) {
obj$data(withSelection = TRUE, withFilter = FALSE, withKey = TRUE)
}

Expand Down
19 changes: 19 additions & 0 deletions inst/examples/plotly.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
library(crosstalk)
library(plotly)
library(leaflet)

sd <- SharedData$new(quakes)

p <- plot_ly(sd, x = ~depth, y = ~mag) %>%
add_markers(alpha = 0.5) %>%
layout(dragmode = "select") %>%
highlight(dynamic = TRUE, persistent = TRUE)

map <- leaflet(sd) %>%
addTiles() %>%
addCircles()

# let leaflet know this is persistent selection
options(persistent = TRUE)

htmltools::browsable(htmltools::tagList(p, map))
53 changes: 38 additions & 15 deletions inst/htmlwidgets/leaflet.js
Original file line number Diff line number Diff line change
Expand Up @@ -818,6 +818,8 @@ var LayerManager = function () {
// Need to save this info so we know what to set opacity to later
layer.options.origOpacity = typeof layer.options.opacity !== "undefined" ? layer.options.opacity : 0.5;
layer.options.origFillOpacity = typeof layer.options.fillOpacity !== "undefined" ? layer.options.fillOpacity : 0.2;
layer.options.origColor = typeof layer.options.color !== "undefined" ? layer.options.color : "#03F";
layer.options.origFillColor = typeof layer.options.fillColor !== "undefined" ? layer.options.fillColor : layer.options.origColor;
}

var ctg = _this._byCrosstalkGroup[ctGroup];
Expand Down Expand Up @@ -856,18 +858,38 @@ var LayerManager = function () {
for (var i = 0; i < groupKeys.length; i++) {
var key = groupKeys[i];
var _layerInfo3 = _this._byStamp[ctg[key]];
_this._setOpacity(_layerInfo3, 1.0);
// reset the crosstalk style params
_layerInfo3.layer.options.ctOpacity = undefined;
_layerInfo3.layer.options.ctFillOpacity = undefined;
_layerInfo3.layer.options.ctColor = undefined;
_layerInfo3.layer.options.ctFillColor = undefined;
_this._setStyle(_layerInfo3);
}
} else {
var selectedKeys = {};
for (var _i3 = 0; _i3 < e.value.length; _i3++) {
selectedKeys[e.value[_i3]] = true;
}
var _groupKeys2 = Object.keys(ctg);
// for compatability with plotly's ability to colour selections
// https://github.com/jcheng5/plotly/blob/71cf8a/R/crosstalk.R#L96-L100
var selectionColour = crosstalk.group(ctGroup).var("plotlySelectionColour").get();
var ctOpts = crosstalk.var("plotlyCrosstalkOpts").get() || { opacityDim: 0.2 };
var persist = ctOpts.persistent === true;
for (var _i4 = 0; _i4 < _groupKeys2.length; _i4++) {
var _key2 = _groupKeys2[_i4];
var _layerInfo4 = _this._byStamp[ctg[_key2]];
_this._setOpacity(_layerInfo4, selectedKeys[_groupKeys2[_i4]] ? 1.0 : 0.2);
var selected = selectedKeys[_groupKeys2[_i4]];
var opts = _layerInfo4.layer.options;

// remember "old" selection colors if this is persistent selection
_layerInfo4.layer.options.ctColor = selected ? selectionColour : persist ? opts.ctColor : opts.origColor;
_layerInfo4.layer.options.ctFillColor = selected ? selectionColour : persist ? opts.ctFillColor : opts.origFillColor;

_layerInfo4.layer.options.ctOpacity = selected ? opts.origOpacity : persist && opts.origOpacity == opts.ctOpacity ? opts.origOpacity : ctOpts.opacityDim * opts.origOpacity;
_layerInfo4.layer.options.ctFillOpacity = selected ? opts.origFillOpacity : persist && opts.origFillOpacity == opts.ctFillOpacity ? opts.origFillOpacity : ctOpts.opacityDim * opts.origFillOpacity;

_this._setStyle(_layerInfo4);
}
}
};
Expand Down Expand Up @@ -943,16 +965,18 @@ var LayerManager = function () {
}
}
}, {
key: "_setOpacity",
value: function _setOpacity(layerInfo, opacity) {
if (layerInfo.layer.setOpacity) {
layerInfo.layer.setOpacity(opacity);
} else if (layerInfo.layer.setStyle) {
layerInfo.layer.setStyle({
opacity: opacity * layerInfo.layer.options.origOpacity,
fillOpacity: opacity * layerInfo.layer.options.origFillOpacity
});
key: "_setStyle",
value: function _setStyle(layerInfo) {
var opts = layerInfo.layer.options;
if (!layerInfo.layer.setStyle) {
return;
}
layerInfo.layer.setStyle({
opacity: opts.ctOpacity || opts.origOpacity,
fillOpacity: opts.ctFillOpacity || opts.origFillOpacity,
color: opts.ctColor || opts.origColor,
fillColor: opts.ctFillColor || opts.origFillColor
});
}
}, {
key: "getLayer",
Expand Down Expand Up @@ -1097,7 +1121,7 @@ var LayerManager = function () {
if (layerInfo.ctGroup) {
var ctGroup = this._byCrosstalkGroup[layerInfo.ctGroup];
var layersForKey = ctGroup[layerInfo.ctKey];
var idx = layersForKey ? layersForKey.indexOf(stamp) : -1;
var idx = layersForKey ? layersForKey.indexOf(+stamp) : -1;
if (idx >= 0) {
if (layersForKey.length === 1) {
delete ctGroup[layerInfo.ctKey];
Expand Down Expand Up @@ -1605,9 +1629,9 @@ methods.addRectangles = function (lat1, lng1, lat2, lng2, layerId, group, option
* @param lat Array of arrays of latitude coordinates for polygons
* @param lng Array of arrays of longitude coordinates for polygons
*/
methods.addPolygons = function (polygons, layerId, group, options, popup, popupOptions, label, labelOptions, highlightOptions) {
methods.addPolygons = function (polygons, layerId, group, options, popup, popupOptions, label, labelOptions, highlightOptions, crosstalkOptions) {
if (polygons.length > 0) {
var df = new _dataframe2.default().col("shapes", polygons).col("layerId", layerId).col("group", group).col("popup", popup).col("popupOptions", popupOptions).col("label", label).col("labelOptions", labelOptions).col("highlightOptions", highlightOptions).cbind(options);
var df = new _dataframe2.default().col("shapes", polygons).col("layerId", layerId).col("group", group).col("popup", popup).col("popupOptions", popupOptions).col("label", label).col("labelOptions", labelOptions).col("highlightOptions", highlightOptions).cbind(options).cbind(crosstalkOptions || {});

addLayers(this, "shape", df, function (df, i) {
// This code used to use L.multiPolygon, but that caused
Expand Down Expand Up @@ -2348,7 +2372,6 @@ function _classCallCheck(instance, Constructor) { if (!(instance instanceof Cons
// pixel of the original image has some contribution to the downscaled image)
// as opposed to a single-step downscaling which will discard a lot of data
// (and with sparse images at small scales can give very surprising results).

var Mipmapper = function () {
function Mipmapper(img) {
_classCallCheck(this, Mipmapper);
Expand Down
57 changes: 46 additions & 11 deletions javascript/src/layer-manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,9 @@ export default class LayerManager {
// Need to save this info so we know what to set opacity to later
layer.options.origOpacity = typeof(layer.options.opacity) !== "undefined" ? layer.options.opacity : 0.5;
layer.options.origFillOpacity = typeof(layer.options.fillOpacity) !== "undefined" ? layer.options.fillOpacity : 0.2;
layer.options.origColor = typeof(layer.options.color) !== "undefined" ? layer.options.color : "#03F";
layer.options.origFillColor = typeof(layer.options.fillColor) !== "undefined" ? layer.options.fillColor : layer.options.origColor;

}

let ctg = this._byCrosstalkGroup[ctGroup];
Expand Down Expand Up @@ -132,18 +135,48 @@ export default class LayerManager {
for (let i = 0; i < groupKeys.length; i++) {
let key = groupKeys[i];
let layerInfo = this._byStamp[ctg[key]];
this._setOpacity(layerInfo, 1.0);
// reset the crosstalk style params
layerInfo.layer.options.ctOpacity = undefined;
layerInfo.layer.options.ctFillOpacity = undefined;
layerInfo.layer.options.ctColor = undefined;
layerInfo.layer.options.ctFillColor = undefined;
this._setStyle(layerInfo);
}
} else {
let selectedKeys = {};
for (let i = 0; i < e.value.length; i++) {
selectedKeys[e.value[i]] = true;
}
let groupKeys = Object.keys(ctg);
// for compatability with plotly's ability to colour selections
// https://github.com/jcheng5/plotly/blob/71cf8a/R/crosstalk.R#L96-L100
let selectionColour = crosstalk.group(ctGroup).var("plotlySelectionColour").get();
let ctOpts = crosstalk.var("plotlyCrosstalkOpts").get() || {opacityDim: 0.2};
let persist = ctOpts.persistent === true;
for (let i = 0; i < groupKeys.length; i++) {
let key = groupKeys[i];
let layerInfo = this._byStamp[ctg[key]];
this._setOpacity(layerInfo, selectedKeys[groupKeys[i]] ? 1.0 : 0.2);
let selected = selectedKeys[groupKeys[i]];
let opts = layerInfo.layer.options;

// remember "old" selection colors if this is persistent selection
layerInfo.layer.options.ctColor =
selected ? selectionColour :
persist ? opts.ctColor : opts.origColor;
layerInfo.layer.options.ctFillColor =
selected ? selectionColour :
persist ? opts.ctFillColor : opts.origFillColor;

layerInfo.layer.options.ctOpacity =
selected ? opts.origOpacity :
(persist && opts.origOpacity == opts.ctOpacity) ? opts.origOpacity :
ctOpts.opacityDim * opts.origOpacity;
layerInfo.layer.options.ctFillOpacity =
selected ? opts.origFillOpacity :
(persist && opts.origFillOpacity == opts.ctFillOpacity) ? opts.origFillOpacity :
ctOpts.opacityDim * opts.origFillOpacity;

this._setStyle(layerInfo);
}
}
};
Expand Down Expand Up @@ -214,15 +247,17 @@ export default class LayerManager {
}
}

_setOpacity(layerInfo, opacity) {
if (layerInfo.layer.setOpacity) {
layerInfo.layer.setOpacity(opacity);
} else if (layerInfo.layer.setStyle) {
layerInfo.layer.setStyle({
opacity: opacity * layerInfo.layer.options.origOpacity,
fillOpacity: opacity * layerInfo.layer.options.origFillOpacity
});
_setStyle(layerInfo) {
let opts = layerInfo.layer.options;
if (!layerInfo.layer.setStyle) {
return;
}
layerInfo.layer.setStyle({
opacity: opts.ctOpacity || opts.origOpacity,
fillOpacity: opts.ctFillOpacity || opts.origFillOpacity,
color: opts.ctColor || opts.origColor,
fillColor: opts.ctFillColor || opts.origFillColor
});
}

getLayer(category, layerId) {
Expand Down Expand Up @@ -349,7 +384,7 @@ export default class LayerManager {
if (layerInfo.ctGroup) {
let ctGroup = this._byCrosstalkGroup[layerInfo.ctGroup];
let layersForKey = ctGroup[layerInfo.ctKey];
let idx = layersForKey ? layersForKey.indexOf(stamp) : -1;
let idx = layersForKey ? layersForKey.indexOf(+stamp) : -1;
if (idx >= 0) {
if (layersForKey.length === 1) {
delete ctGroup[layerInfo.ctKey];
Expand Down
5 changes: 3 additions & 2 deletions javascript/src/methods.js
Original file line number Diff line number Diff line change
Expand Up @@ -521,7 +521,7 @@ methods.addRectangles = function(lat1, lng1, lat2, lng2, layerId, group, options
* @param lat Array of arrays of latitude coordinates for polygons
* @param lng Array of arrays of longitude coordinates for polygons
*/
methods.addPolygons = function(polygons, layerId, group, options, popup, popupOptions, label, labelOptions, highlightOptions) {
methods.addPolygons = function(polygons, layerId, group, options, popup, popupOptions, label, labelOptions, highlightOptions, crosstalkOptions) {
if(polygons.length>0) {
let df = new DataFrame()
.col("shapes", polygons)
Expand All @@ -532,7 +532,8 @@ methods.addPolygons = function(polygons, layerId, group, options, popup, popupOp
.col("label", label)
.col("labelOptions", labelOptions)
.col("highlightOptions", highlightOptions)
.cbind(options);
.cbind(options)
.cbind(crosstalkOptions || {});

addLayers(this, "shape", df, function(df, i) {
// This code used to use L.multiPolygon, but that caused
Expand Down
1 change: 0 additions & 1 deletion man/addAwesomeMarkers.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion man/addGraticule.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion man/addLayersControl.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion man/addLegend.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion man/addMeasure.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion man/addMiniMap.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion man/addProviderTiles.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion man/addRasterImage.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 1 addition & 2 deletions man/addScaleBar.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion man/addSimpleGraticule.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion man/addTerminator.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion man/atlStorms2005.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion man/awesomeIconList.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion man/awesomeIcons.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion man/breweries91.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions man/colorNumeric.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion man/deprecated.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion man/derivePoints.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading