javascript - R传单中的饼图,将计数变为总和并控制更多大小

标签 javascript r leaflet r-leaflet leaflet.markercluster

我对分组标记和以小饼图形式按组显示计数的解决方案很着迷https://stackoverflow.com/a/60525137/3480717
我只是R,不知道JS。我希望代码对每个数据点的值求和而不是计数(每个单独的数据点可能已经代表一个计数)。而且我想根据值控制更多气泡的大小。
你能帮我看看如何更改 js 代码,以便它对数据点的值求和,以及如何增加/控制气泡的大小吗?
这里有一个对非饼标记求和而不是计数的理想解决方案:How to display the value (sum) rather than count of markers in a dc.leaflet.js
这里有一个也可以控制气泡大小的解决方案:Clustered leaflet markers with sum (not count) totals: how to get consistent round red shape and label format like in unclustered markers
原码是https://stackoverflow.com/users/2711712/danielbonnery @DanielBonnery

library(leaflet)
library(dplyr)
#Creates data
data("breweries91",package="leaflet")
#set.seed(1);
breweries91$goodbear<-sample(as.factor(c("terrific","marvelous","culparterretaping")),nrow(breweries91),replace=T)
#Colors
joliepalette<-c("red","green","blue")[1:nlevels(breweries91$goodbear)]
getColor <- function(breweries91) {joliepalette[breweries91$goodbear]}

icons <- awesomeIcons(
  icon = 'ios-close',
  iconColor = 'black',
  library = 'ion',
  markerColor = getColor(breweries91)
)

#Generate the javascript

jsscript3<-
  paste0(
"function(cluster) {
const groups= [",paste("'",levels(breweries91$goodbear),"'",sep="",collapse=","),"];
const colors= {
groups: [",paste("'",joliepalette,"'",sep="",collapse=","),"],
center:'#ddd',
text:'black'
};
const markers= cluster.getAllChildMarkers();

const proportions= groups.map(group => markers.filter(marker => marker.options.group === group).length / markers.length);
function sum(arr, first= 0, last) {
return arr.slice(first, last).reduce((total, curr) => total+curr, 0);
}
const cumulativeProportions= proportions.map((val, i, arr) => sum(arr, 0, i+1));
cumulativeProportions.unshift(0);

const width = 2*Math.sqrt(markers.length);
const radius= 15+width/2;

const arcs= cumulativeProportions.map((prop, i) => { return {
x   :  radius*Math.sin(2*Math.PI*prop),
y   : -radius*Math.cos(2*Math.PI*prop),
long: proportions[i-1] >.5 ? 1 : 0
}});
const paths= proportions.map((prop, i) => {
if (prop === 0) return '';
else if (prop === 1) return `<circle cx='0' cy='0' r='${radius}' fill='none' stroke='${colors.groups[i]}' stroke-width='${width}' stroke-alignment='center' stroke-linecap='butt' />`;
else return `<path d='M ${arcs[i].x} ${arcs[i].y} A ${radius} ${radius} 0 ${arcs[i+1].long} 1 ${arcs[i+1].x} ${arcs[i+1].y}' fill='none' stroke='${colors.groups[i]}' stroke-width='${width}' stroke-alignment='center' stroke-linecap='butt' />`
});

return new L.DivIcon({
html: `
<svg width='60' height='60' viewBox='-30 -30 60 60' style='width: 60px; height: 60px; position: relative; top: -24px; left: -24px;' >
<circle cx='0' cy='0' r='15' stroke='none' fill='${colors.center}' />
<text x='0' y='0' dominant-baseline='central' text-anchor='middle' fill='${colors.text}' font-size='15'>${markers.length}</text>
${paths.join('')}
</svg>
`,
className: 'marker-cluster'
});
}")

# Generates the map.
leaflet() %>%
  addTiles() %>%
  addAwesomeMarkers(data=breweries91,
                    group=~goodbear,
                    icon = icons,
                    clusterOptions = markerClusterOptions(
                      iconCreateFunction =
                        JS(jsscript3)))

最佳答案

I would like the code to sum values from each data point and not count.


您可以使用 markerOptions() 将数据/统计信息传递给标记。
leaflet() %>%
  addTiles() %>%
    addMarkers(
      options = markerOptions(score = getScore(breweries91)),
      ...
    )
然后在 JavaScript 中使用 marker.options.score检索它。

And I would like to control more the size of the bubbles dependent on the value.


在下面的演示代码中,在 javascript 中,查找代码 let width = 4 + 2*Math.sqrt(grandTotal/1.5);玩它来调整气泡半径。

... count for non-pie markers


可用,R 的传单,初始化器 makeIcon , awesomeIcons , 和 icons强制您创建和使用自定义图像。而且没有办法绕过它。 addCircleMarkers看起来对我们的目的很有用,但它不允许您设置文本。
我们可以使用singleMarkerMode在集群选项中。使用它,我们可以使单个标记成为大小为 1 的簇,并使用我们已经编码的 iconCreateFunction 渲染它们JavaScript 代码:
leaflet() %>%
  addTiles() %>%
   addMarkers(
             ...
             clusterOptions = markerClusterOptions(
               singleMarkerMode = TRUE,
               iconCreateFunction = JS(jsscript3)
             )
  )

1) 演示:在您的代码中,没有可用于标记的可用数字数据。所以我创建了一个映射,使用 getScore函数,对于分值:
terrific          => 3
marvelous         => 2
culparterretaping => 1
使用 summarytools 添加带有摘要的图例.
这是最终代码:
# Title: R Leaflet custom summing marker demo


# Load packages ##################
install.packages("pacman")
require(pacman)

pacman::p_load(pacman, dplyr, leaflet, summarytools)


# Creates data ##################
data("breweries91",package="leaflet")
cat('\014') # ctrl+L
#head(breweries91, 2L)
breweries91$goodbeer<-sample(as.factor(c("terrific","marvelous","culparterretaping")),nrow(breweries91),replace=T)
names(breweries91)

# Colors
joliepalette<-c("darkviolet","orangered","lime")[1:nlevels(breweries91$goodbeer)]
getColor <- function(breweries91) {joliepalette[breweries91$goodbeer]}

# Score
jolieValue<-c(1L,2L,3L)[1:nlevels(breweries91$goodbeer)]
getScore <- function(breweries91) {jolieValue[breweries91$goodbeer]}


# iconCreateFunction Javascript
jsscript3<-paste0(
  "function(cluster) {
   const groups= [",paste("'",levels(breweries91$goodbeer),"'",sep="",collapse=","),"];
   const colors= {
     groups: [",paste("'",joliepalette,"'",sep="",collapse=","),"],
     center:'#ddd',
     text:'black'
    };
   const markers= cluster.getAllChildMarkers();
   let grandTotal = markers.reduce((a,b)=> +a + +b.options.score, 0);

   const proportions= groups.map(group => markers
                        .filter(marker => marker.options.group === group)
                         .reduce((a,b)=> +a + +b.options.score, 0) / grandTotal);

   function sum(arr, first= 0, last) {
    return arr.slice(first, last).reduce((total, curr) => total+curr, 0);
  }
  const cumulativeProportions= proportions.map((val, i, arr) => sum(arr, 0, i+1));
  cumulativeProportions.unshift(0);

  let width = 4 + 2*Math.sqrt(grandTotal/1.5);
  width = width > 16? 16: width;
  let radius= 10 + (width/2);
  radius += (grandTotal < 40)? grandTotal/10 : 4;

  const arcs= cumulativeProportions.map((prop, i) => { return {
    x   :  radius*Math.sin(2*Math.PI*prop),
    y   : -radius*Math.cos(2*Math.PI*prop),
    long: proportions[i-1] >.5 ? 1 : 0
   }});
 const paths= proportions.map((prop, i) => {
   if (prop === 0) return '';
   else if (prop === 1) return `<circle cx='0' cy='0' r='${radius-2}' fill-opacity='0.3' stroke-opacity fill='${colors.groups[i]}' stroke='${colors.groups[i]}' stroke-width='${width}' stroke-alignment='center' stroke-linecap='butt' />`;
   else return `<path d='M ${arcs[i].x} ${arcs[i].y} A ${radius} ${radius} 0 ${arcs[i+1].long} 1 ${arcs[i+1].x} ${arcs[i+1].y}' fill='none' stroke='${colors.groups[i]}' stroke-width='${width}' stroke-alignment='center' stroke-linecap='butt' />`
  });

  return new L.DivIcon({
   html: `
    <svg width='60' height='60' viewBox='-30 -30 60 60' style='width: 60px; height: 60px; position: relative; top: -24px; left: -24px;' >
      <circle cx='0' cy='0' r='15' stroke='none' fill='${colors.center}' />
      ${paths.join('')}
      <text x='0' y='0' dominant-baseline='central' text-anchor='middle' fill='${colors.text}' font-size='16'>${grandTotal}</text>
    </svg>
    `,
   className: 'marker-cluster'
   });
}")

# gather stats for legend
myStat <- freq(breweries91$goodbeer, report.nas = FALSE, cumul = FALSE)
s1 <- paste("3 - terrific(",myStat[3,1],")")
s2 <- paste("2 - marvelous(",myStat[2,1],")")
s3 <- paste("1 - culparterretaping(", myStat[1,1],")")


# generates the map.
leaflet() %>%
  addTiles() %>%
  addLegend("topright", 
            colors = c("lime", "orangered", "darkviolet"),
            labels = c(s1, s2,s3),
            title = "Beer goodness:",
            opacity = 1) %>%
  addMarkers(data = breweries91,
             group = ~goodbeer,
             options = markerOptions(score = getScore(breweries91)),
             clusterOptions = markerClusterOptions(
               singleMarkerMode = TRUE,
               iconCreateFunction = JS(jsscript3)
             )
  )


# print stats
freq(breweries91$goodbeer, report.nas = FALSE, cumul = FALSE)
print(paste("Grand Score: ", myStat[1,1]*1 + myStat[2,1]*2 + myStat[3,1]*3))


截屏:
enter image description here

2) 共享集群对象中的数据:每个标记都可以看到它的弹出对象和弹出选项。因此,您可以从标记对象本身中操作弹出窗口,在我们的例子中是 iconCreateFunction .

MarkerCluster库不提供为集群标记添加标签或弹出窗口的方法。当您单击群集图标时,它会放大并且不显示弹出窗口。作为一种解决方法,我们可以禁用点击放大并显示自定义工具提示/弹出窗口:

# Title: R Leaflet custom summing marker demo


# Load packages ##################
install.packages("pacman")
require(pacman)

pacman::p_load(pacman, dplyr, leaflet, summarytools)


# Creates data ##################
data("breweries91",package="leaflet")
cat('\014') # ctrl+L
#head(breweries91, 2L)
breweries91$goodbeer<-sample(as.factor(c("terrific","marvelous","culparterretaping")),nrow(breweries91),replace=T)
names(breweries91)

# Colors
joliepalette<-c("darkviolet","orangered","limegreen")[1:nlevels(breweries91$goodbeer)]
getColor <- function(breweries91) {joliepalette[breweries91$goodbeer]}

# Score
jolieValue<-c(1L,2L,3L)[1:nlevels(breweries91$goodbeer)]
getScore <- function(breweries91) {jolieValue[breweries91$goodbeer]}


# iconCreateFunction Javascript
jsscript3<-paste0(
  "function(cluster) {
   const groups= [",paste("'",levels(breweries91$goodbeer),"'",sep="",collapse=","),"];
   const colors= {
     groups: [",paste("'",joliepalette,"'",sep="",collapse=","),"],
     center:'#ddd',
     text:'black'
    };
   const markers= cluster.getAllChildMarkers();
   let grandTotal = markers.reduce((a,b)=> +a + +b.options.score, 0);

   const proportions= groups.map(group => markers
                        .filter(marker => marker.options.group === group)
                         .reduce((a,b)=> +a + +b.options.score, 0) / grandTotal);

   function sum(arr, first= 0, last) {
    return arr.slice(first, last).reduce((total, curr) => total+curr, 0);
  }
  const cumulativeProportions= proportions.map((val, i, arr) => sum(arr, 0, i+1));
  cumulativeProportions.unshift(0);

  let width = 4 + 2*Math.sqrt(grandTotal/1.5);
  width = width > 16? 16: width;
  let radius= 10 + (width/2);
  radius += (grandTotal < 40)? grandTotal/10 : 4;

  const arcs= cumulativeProportions.map((prop, i) => { return {
    x   :  radius*Math.sin(2*Math.PI*prop),
    y   : -radius*Math.cos(2*Math.PI*prop),
    long: proportions[i-1] >.5 ? 1 : 0
   }});
 const paths= proportions.map((prop, i) => {
   if (prop === 0) return '';
   else if (prop === 1) return `<circle cx='0' cy='0' r='${radius-2}' fill-opacity='0.3' stroke-opacity fill='${colors.groups[i]}' stroke='${colors.groups[i]}' stroke-width='${width}' stroke-alignment='center' stroke-linecap='butt' />`;
   else return `<path d='M ${arcs[i].x} ${arcs[i].y} A ${radius} ${radius} 0 ${arcs[i+1].long} 1 ${arcs[i+1].x} ${arcs[i+1].y}' fill='none' stroke='${colors.groups[i]}' stroke-width='${width}' stroke-alignment='center' stroke-linecap='butt' />`
  });

  let icon = new L.DivIcon({
   html: `
    <svg width='60' height='60' viewBox='-30 -30 60 60' style='width: 60px; height: 60px; position: relative; top: -24px; left: -24px;' >
      <circle cx='0' cy='0' r='15' stroke='none' fill='${colors.center}' />
      ${paths.join('')}
      <text x='0' y='0' dominant-baseline='central' text-anchor='middle' fill='${colors.text}' font-size='16'>${grandTotal}</text>
    </svg>
    `,
   className: 'marker-cluster'
   });
   
   // make custom popup for the cluster
   if(cluster._map){
     let counts =  groups.map(group => markers
                        .filter(marker => marker.options.group === group).length);
     let content = `<span style='color:limegreen'>${groups[2]}</span>&nbsp;<span>(${counts[2]})</span><br>`;
     content += `<span style='color:orangered'>${groups[1]}</span>&nbsp;<span>(${counts[1]})</span><br>`;
     content += `<span style='color:darkviolet'>${groups[0]}</span>&nbsp;<span>(${counts[0]})</span><br>`;
     content = `<div style='font-size:13px;font-weight:bold'>` + content + `</div>`;

      cluster.on('click', function (a) {
          let tt = L.tooltip({permanent:false, direction:'top', offset: L.point(0, -width*2)});
        tt.setContent( content );
        tt.setLatLng(cluster.getLatLng());
        tt.addTo(cluster._map);
      });
   }
   
   return icon;
}")

# gather stats for legend
myStat <- freq(breweries91$goodbeer, report.nas = FALSE, cumul = FALSE)
s1 <- paste("3 - terrific(",myStat[3,1],")")
s2 <- paste("2 - marvelous(",myStat[2,1],")")
s3 <- paste("1 - culparterretaping(", myStat[1,1],")")


# generates the map.
leaflet() %>%
  addTiles() %>%
  addLegend("topright", 
            colors = c("limegreen", "orangered", "darkviolet"),
            labels = c(s1, s2,s3),
            title = "Beer goodness:",
            opacity = 1) %>%
  addMarkers(data = breweries91,
             group = ~goodbeer,
             popup = paste("", breweries91$goodbeer),
             popupOptions = popupOptions(maxWidth = 1000, closeOnClick = TRUE),
             options = markerOptions(score = getScore(breweries91)),
             clusterOptions = markerClusterOptions(
               singleMarkerMode = TRUE,
               zoomToBoundsOnClick = FALSE,
               iconCreateFunction = JS(jsscript3)
             )
  )


# print stats
cat('\014') # ctrl+L
freq(breweries91$goodbeer, report.nas = FALSE, cumul = FALSE)
print(paste("Grand Score: ", myStat[1,1]*1 + myStat[2,1]*2 + myStat[3,1]*3))

enter image description here
在这里,我们使用常规方法在单个标记上显示了弹出窗口:
leaflet() %>%
  addTiles() %>%
    addMarkers(data = breweries91,
             popup = paste("", breweries91$goodbeer),
             popupOptions = popupOptions(maxWidth = 1000, closeOnClick = TRUE),
    ...)
iconCreateFunction 中完成的特殊处理仅用于集群标记。

在最坏的情况下,如果您想在标记/集群之间共享数据,那么您可以找到 Leaflet map objectcluster._map属性(property)。或者您可以将数据附加到 javascript window对象并通过 javascript 代码在任何地方访问它。

3) 将整行传递给传单:我们可以使用以下方法将整行传递给传单:
options = markerOptions(row_data = setNames(split(breweries91,
                                             seq(nrow(breweries91))),
                                              rownames(breweries91)))
有了这个,我们现在可以在弹出窗口中有很多东西,包括坐标:

# Title: R Leaflet custom summing marker demo


# Load packages ##################
install.packages("pacman")
require(pacman)

pacman::p_load(pacman, dplyr, leaflet, summarytools)


# Creates data ##################
data("breweries91",package="leaflet")
cat('\014') # ctrl+L
#head(breweries91, 2L)
breweries91$goodbeer<-sample(as.factor(c("terrific","marvelous","culparterretaping")),nrow(breweries91),replace=T)
breweries91$score<-ifelse(breweries91$goodbeer == "terrific",3L,
                                                ifelse(breweries91$goodbeer == "marvelous",2L,
                                                       ifelse(breweries91$goodbeer == "culparterretaping",1L,0L)))
names(breweries91)

# Colors
joliepalette<-c("darkviolet","orangered","limegreen")[1:nlevels(breweries91$goodbeer)]
getColor <- function(breweries91) {joliepalette[breweries91$goodbeer]}

# iconCreateFunction Javascript
jsscript3<-paste0(
  "function(cluster) {
   const groups= [",paste("'",levels(breweries91$goodbeer),"'",sep="",collapse=","),"];
   const colors= {
     groups: [",paste("'",joliepalette,"'",sep="",collapse=","),"],
     center:'#ddd',
     text:'black'
    };
   const markers= cluster.getAllChildMarkers();
   //console.log(markers[0]);
   let grandTotal = markers.reduce((a,b)=> +a + +b.options.row_data.data.score, 0);
    
   const proportions= groups.map(group => markers
                        .filter(marker => marker.options.group === group)
                         .reduce((a,b)=> +a + +b.options.row_data.data.score, 0) / grandTotal);

   function sum(arr, first= 0, last) {
    return arr.slice(first, last).reduce((total, curr) => total+curr, 0);
  }
  const cumulativeProportions= proportions.map((val, i, arr) => sum(arr, 0, i+1));
  cumulativeProportions.unshift(0);

  let width = 4 + 2*Math.sqrt(grandTotal/1.5);
  width = width > 16? 16: width;
  let radius= 10 + (width/2);
  radius += (grandTotal < 40)? grandTotal/10 : 4;

  const arcs= cumulativeProportions.map((prop, i) => { return {
    x   :  radius*Math.sin(2*Math.PI*prop),
    y   : -radius*Math.cos(2*Math.PI*prop),
    long: proportions[i-1] >.5 ? 1 : 0
   }});
 const paths= proportions.map((prop, i) => {
   if (prop === 0) return '';
   else if (prop === 1) return `<circle cx='0' cy='0' r='${radius-2}' fill-opacity='0.3' stroke-opacity fill='${colors.groups[i]}' stroke='${colors.groups[i]}' stroke-width='${width}' stroke-alignment='center' stroke-linecap='butt' />`;
   else return `<path d='M ${arcs[i].x} ${arcs[i].y} A ${radius} ${radius} 0 ${arcs[i+1].long} 1 ${arcs[i+1].x} ${arcs[i+1].y}' fill='none' stroke='${colors.groups[i]}' stroke-width='${width}' stroke-alignment='center' stroke-linecap='butt' />`
  });

  let icon = new L.DivIcon({
   html: `
    <svg width='60' height='60' viewBox='-30 -30 60 60' style='width: 60px; height: 60px; position: relative; top: -24px; left: -24px;' >
      <circle cx='0' cy='0' r='15' stroke='none' fill='${colors.center}' />
      ${paths.join('')}
      <text x='0' y='0' dominant-baseline='central' text-anchor='middle' fill='${colors.text}' font-size='16'>${grandTotal}</text>
    </svg>
    `,
   className: 'marker-cluster'
   });
   
   // make custom popup for the cluster
   let content ='popup';
   if(cluster._map){ //if it's cluster
   //console.log(cluster);
     let counts =  groups.map(group => markers
                        .filter(marker => marker.options.group === group).length);
     content = `<span style='color:limegreen'>${groups[2]}</span>&nbsp;<span>(${counts[2]})</span><br>`;
     content += `<span style='color:orangered'>${groups[1]}</span>&nbsp;<span>(${counts[1]})</span><br>`;
     content += `<span style='color:darkviolet'>${groups[0]}</span>&nbsp;<span>(${counts[0]})</span><br>`;
     content = `<div style='font-size:13px;font-weight:bold'>` + content + `</div>`;

      
   } else{ //if it's single marker
    //console.log(cluster.getAllChildMarkers());
    cluster = cluster.getAllChildMarkers()[0];
    //console.log(cluster);
    let r = cluster.options.row_data;
    let loc = r.coords;
    r = r.data;
    let address = r.address;
    let name = `${r.brewery} (${r.founded})`;
    content = `<span><strong>${name}</strong></span><br>`;
    content += `<span style='margin-bottom:10px'>[${loc}]</span><hr>`;
    content += `<span>${address}, ${r.village}, ${r.zipcode}</span><br>`;
    content += `<span>Goodness: ${r.goodbeer}</span>`;
   }
   cluster.on('click', function (a) {
          let tt = L.tooltip({permanent:false, direction:'top', offset: L.point(0, -width*2)});
        tt.setContent( content );
        tt.setLatLng(cluster.getLatLng());
        tt.addTo(cluster._map);
      });
   
   return icon;
}")

# gather stats for legend
myStat <- freq(breweries91$goodbeer, report.nas = FALSE, cumul = FALSE)
s1 <- paste("3 - terrific(",myStat[3,1],")")
s2 <- paste("2 - marvelous(",myStat[2,1],")")
s3 <- paste("1 - culparterretaping(", myStat[1,1],")")


# generates the map.
leaflet() %>%
  addTiles() %>%
  addLegend("topright", 
            colors = c("limegreen", "orangered", "darkviolet"),
            labels = c(s1, s2,s3),
            title = "Beer goodness:",
            opacity = 1) %>%
  addMarkers(data = breweries91,
             group = ~goodbeer,
             popupOptions = popupOptions(maxWidth = 1000, closeOnClick = TRUE),
             options = markerOptions(row_data = setNames(split(breweries91, seq(nrow(breweries91))), rownames(breweries91))),
             clusterOptions = markerClusterOptions(
               singleMarkerMode = TRUE,
               zoomToBoundsOnClick = FALSE,
               iconCreateFunction = JS(jsscript3)
             )
  )


# print stats
cat('\014') # ctrl+L
freq(breweries91$goodbeer, report.nas = FALSE, cumul = FALSE)
print(paste("Grand Score: ", myStat[1,1]*1 + myStat[2,1]*2 + myStat[3,1]*3))

enter image description here

作为引用,我提取了一个示例集群对象:Cluster Object Json .
使用它,您可以从集群对象中找到要在 javascript 中使用的属性。

关于javascript - R传单中的饼图,将计数变为总和并控制更多大小,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69880307/

相关文章:

javascript - Draw.io:获取 EditorUI 的实例

javascript - 如何为react组件方法绑定(bind) "this"

javascript - Angular 和 Leaflet 在点击时添加标记并获取 Coord

javascript - 使用 shp-write 将 Geojson 转换为 Javascript 中的 Shapefile

R Shiny - 使用文件输入将标记添加到传单 map

javascript - 如何从外部暂停和停止递归javascript函数

c# - 使用javascript绑定(bind)gridview?

r - 为什么我的泊松回归的似然/AIC 是无限的?

r - 如何在 R 中计算变差函数时更改滞后距离

r - Purrr 映射多个函数和两个输入