From ec0bfe645d24e6817f3da3d702b210ef36236906 Mon Sep 17 00:00:00 2001 From: trafficonese <35805180+trafficonese@users.noreply.github.com> Date: Sun, 1 Sep 2024 12:42:31 +0000 Subject: [PATCH] =?UTF-8?q?Deploying=20to=20gh-pages=20from=20@=20traffico?= =?UTF-8?q?nese/leaflet.extras2@b3f2b8660734991bdd88459a590356633e9c70c9?= =?UTF-8?q?=20=F0=9F=9A=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dev/index.html | 1 + dev/news/index.html | 3 + dev/pkgdown.yml | 2 +- dev/reference/addClusterCharts.html | 329 +++++++++ dev/reference/addContextmenu.html | 4 +- dev/reference/addEasyprint.html | 4 +- dev/reference/addGIBS.html | 4 +- dev/reference/addHexbin.html | 4 +- dev/reference/addHistory.html | 4 +- dev/reference/addMapkeyMarkers.html | 4 +- dev/reference/addSidebyside.html | 4 +- dev/reference/addSpinner.html | 4 +- dev/reference/addTimeslider.html | 1 - dev/reference/addWMS.html | 4 +- dev/reference/clusterchartOptions.html | 158 +++++ dev/reference/historyOptions.html | 4 +- dev/reference/index.html | 12 + .../MIT-LICENCE.txt | 20 + .../MarkerCluster.Default.css | 60 ++ .../MarkerCluster.css | 14 + .../leaflet.markercluster.freezable.js | 6 + .../leaflet.markercluster.js | 7 + .../leaflet.markercluster.layersupport.js | 6 + .../leaflet-markercluster-1.0.5/package.json | 22 + .../libs/lfx-clustercharts-1.0.0/d3.v3.min.js | 5 + .../lfx-clustercharts-bindings.js | 660 ++++++++++++++++++ .../lfx-clustercharts.css | 50 ++ .../lfx-clustercharts-css-1.0.0/d3.v3.min.js | 5 + .../lfx-clustercharts-bindings.js | 660 ++++++++++++++++++ .../lfx-clustercharts.css | 50 ++ dev/sitemap.xml | 2 + 31 files changed, 2091 insertions(+), 22 deletions(-) create mode 100644 dev/reference/addClusterCharts.html create mode 100644 dev/reference/clusterchartOptions.html create mode 100644 dev/reference/libs/leaflet-markercluster-1.0.5/MIT-LICENCE.txt create mode 100644 dev/reference/libs/leaflet-markercluster-1.0.5/MarkerCluster.Default.css create mode 100644 dev/reference/libs/leaflet-markercluster-1.0.5/MarkerCluster.css create mode 100644 dev/reference/libs/leaflet-markercluster-1.0.5/leaflet.markercluster.freezable.js create mode 100644 dev/reference/libs/leaflet-markercluster-1.0.5/leaflet.markercluster.js create mode 100644 dev/reference/libs/leaflet-markercluster-1.0.5/leaflet.markercluster.layersupport.js create mode 100644 dev/reference/libs/leaflet-markercluster-1.0.5/package.json create mode 100644 dev/reference/libs/lfx-clustercharts-1.0.0/d3.v3.min.js create mode 100644 dev/reference/libs/lfx-clustercharts-1.0.0/lfx-clustercharts-bindings.js create mode 100644 dev/reference/libs/lfx-clustercharts-1.0.0/lfx-clustercharts.css create mode 100644 dev/reference/libs/lfx-clustercharts-css-1.0.0/d3.v3.min.js create mode 100644 dev/reference/libs/lfx-clustercharts-css-1.0.0/lfx-clustercharts-bindings.js create mode 100644 dev/reference/libs/lfx-clustercharts-css-1.0.0/lfx-clustercharts.css diff --git a/dev/index.html b/dev/index.html index aebcc1b0..cbc934c7 100644 --- a/dev/index.html +++ b/dev/index.html @@ -109,6 +109,7 @@
sidebar_tabs
ID....
in antpathOptions
to be able to set the pane (e.g.: renderer= JS('L.svg({pane: "my-pane"})')
)clusterCharts
using Leaflet.markercluster
and d3
for piechart and barcharts.addClusterCharts
to enable pie and bar charts in Marker clusters using Leaflet.markercluster
, d3
and L.DivIcon
, with support for customizable category styling and various aggregation methods like sum, min, max, mean, and median.geojsonsf
to yyjsonr
(heightgraph, timeslider, clustercharts)Clusters markers on a Leaflet map and visualizes them using
+customizable charts, such as pie or bar charts, showing counts by category.
+When using the "custom"
type, a pie chart is rendered with aggregated data,
+employing methods like sum, min, max, mean, or median.
addClusterCharts(
+ map,
+ layerId = NULL,
+ group = NULL,
+ type = c("pie", "bar", "horizontal", "custom"),
+ aggregation = c("sum", "min", "max", "mean", "median"),
+ valueField = NULL,
+ options = clusterchartOptions(),
+ icon = NULL,
+ html = NULL,
+ popup = NULL,
+ popupOptions = NULL,
+ label = NULL,
+ labelOptions = NULL,
+ clusterOptions = NULL,
+ clusterId = NULL,
+ categoryField,
+ categoryMap,
+ popupFields = NULL,
+ popupLabels = NULL,
+ markerOptions = NULL,
+ legendOptions = list(title = "", position = "topright"),
+ data = getMapData(map)
+)
a map widget object created from leaflet()
the layer id
the name of the group the newly created layers should belong to
+(for clearGroup
and addLayersControl
purposes).
+Human-friendly group names are permitted–they need not be short,
+identifier-style names. Any number of layers and even different types of
+layers (e.g. markers and polygons) can share the same group name.
The type of chart to use for clusters: "pie"
, "bar"
, "horizontal"
, or "custom"
.
Aggregation method for "custom"
charts (e.g., sum, min, max, mean, median).
Column name with values to aggregate for "custom"
charts.
Additional options for cluster charts (see clusterchartOptions
).
An icon or set of icons to include, created with makeIcon
or iconList
.
The column name containing the HTML content to include in the markers.
The column name used to retrieve feature properties for the popup.
A Vector of popupOptions
to provide popups
a character vector of the HTML content for the labels
A Vector of labelOptions
to provide label
+options for each label. Default NULL
if not NULL
, markers will be clustered using
+Leaflet.markercluster;
+ you can use markerClusterOptions()
to specify marker cluster
+options
the id for the marker cluster layer
Column name for categorizing charts.
A data.frame mapping categories to chart properties (e.g., label, color, icons, stroke).
A string or vector of strings indicating the column names to include in popups.
A string or vector of strings indicating the labels for the popup fields.
Additional options for markers (see markerOptions::markerOptions()
).
A list of options for the legend, including the title and position.
the data object from which the argument values are derived; by
+default, it is the data
object provided to leaflet()
+initially, but can be overridden
The `clusterCharts` use Leaflet's `L.DivIcon`, allowing you to fully customize +the styling of individual markers and clusters using CSS. Each individual marker +within a cluster is assigned the CSS class `clustermarker`, while the entire +cluster is assigned the class `clustermarker-cluster`. You can modify the appearance +of these elements by targeting these classes in your custom CSS.
+Other clusterCharts:
+clusterchartOptions()
# Example usage:
+library(sf)
+#> Linking to GEOS 3.10.2, GDAL 3.4.1, PROJ 8.2.1; sf_use_s2() is TRUE
+library(leaflet)
+library(leaflet.extras2)
+
+data <- sf::st_as_sf(breweries91)
+categories <- c("Schwer", "Mäßig", "Leicht", "kein Schaden")
+data$category <- sample(categories, size = nrow(data), replace = TRUE)
+
+## Pie Chart
+leaflet() %>%
+ addProviderTiles("CartoDB.Positron") %>%
+ leaflet::addLayersControl(overlayGroups = "clustermarkers") %>%
+ addClusterCharts(data = data
+ , categoryField = "category"
+ , categoryMap = data.frame(labels = categories,
+ colors = c("#F88", "#FA0", "#FF3", "#BFB"),
+ strokes = "gray")
+ , group = "clustermarkers"
+ , popupFields = c("brewery", "address", "zipcode", "category")
+ , popupLabels = c("Brauerei", "Adresse", "PLZ", "Art")
+ , label = "brewery"
+ )
+
+
+## Bar Chart
+leaflet() %>%
+ addProviderTiles("CartoDB.Positron") %>%
+ leaflet::addLayersControl(overlayGroups = "clustermarkers") %>%
+ addClusterCharts(data = data
+ , type = "bar"
+ , categoryField = "category"
+ , categoryMap = data.frame(labels = categories,
+ colors = c("#F88", "#FA0", "#FF3", "#BFB"),
+ strokes = "gray")
+ , group = "clustermarkers"
+ , popupFields = c("brewery", "address", "zipcode", "category")
+ , popupLabels = c("Brauerei", "Adresse", "PLZ", "Art")
+ , label = "brewery")
+
+
+## Custom Pie Chart with "mean" aggregation on column "value"
+data <- sf::st_as_sf(breweries91)
+categories <- c("Schwer", "Mäßig", "Leicht", "kein Schaden")
+data$category <- sample(categories, size = nrow(data), replace = TRUE)
+data$value <- round(runif(nrow(data), 0, 100), 0)
+
+leaflet() %>%
+ addProviderTiles("CartoDB.Positron") %>%
+ leaflet::addLayersControl(overlayGroups = "clustermarkers") %>%
+ addClusterCharts(data = data
+ , type = "custom"
+ , valueField = "value"
+ , aggregation = "mean"
+ , categoryField = "category"
+ , categoryMap = data.frame(labels = categories,
+ colors = c("#F88", "#FA0", "#FF3", "#BFB"),
+ strokes = "gray")
+ , options = clusterchartOptions(rmax=50, digits=0, innerRadius = 20)
+ , group = "clustermarkers"
+ , popupFields = c("brewery", "address", "zipcode", "category","value")
+ , popupLabels = c("Brauerei", "Adresse", "PLZ", "Art", "Value")
+ , label = "brewery"
+ )
+
+
+## For Shiny examples, please run:
+# runApp(system.file("examples/clusterCharts_app.R", package = "leaflet.extras2"))
+# runApp(system.file("examples/clustercharts_sum.R", package = "leaflet.extras2"))
+
Adds options for clusterCharts
+clusterchartOptions(
+ rmax = 30,
+ size = c(20, 20),
+ width = 40,
+ height = 50,
+ strokeWidth = 1,
+ innerRadius = 10,
+ labelBackground = FALSE,
+ labelFill = "white",
+ labelStroke = "black",
+ labelColor = "black",
+ labelOpacity = 0.9,
+ digits = 2,
+ sortTitlebyCount = TRUE
+)
The maximum radius of the clusters.
The size of the cluster markers.
The width of the bar-charts.
The height of the bar-charts.
The stroke width of the chart.
The inner radius of pie-charts.
Should the label have a background? Default is `FALSE`
The label background color. Default is `white`
The label stroke color. Default is `black`
The label color. Default is `black`
The label color. Default is `0.9`
The amount of digits. Default is `2`
Should the svg-title be sorted by count or by the categories.
Other clusterCharts:
+addClusterCharts()
markermenuItems()
markermenuItems
addClusterCharts
clusterchartOptions
0)for(u=-1;++u';
+ popupFields.map( function(key, idx) {
+ if (props[key]) {
+ var val = props[key],
+ label = popupLabels[idx];
+ popupContent += '
';
+ }
+
+ if (popupContent !== '') {
+ if (popupOptions !== null){
+ layer.bindPopup(popupContent, popupOptions);
+ } else {
+ layer.bindPopup(popupContent);
+ }
+ }
+ }
+ function defineClusterIcon(cluster) {
+ var children = cluster.getAllChildMarkers(),
+ n = children.length,
+ r = rmax - 2 * strokeWidth - (n<10?12:n<100?8:n<1000?4:0),
+ iconDim = (r + strokeWidth) * 2,
+ html,
+ innerRadius = options.innerRadius ? options.innerRadius : -10;
+
+ //bake some svg markup
+ if (type == "custom") {
+
+ // Define aggregation function
+ function aggregateData(data, categoryField, aggregationFunc, valueField) {
+ return d3.nest()
+ .key(function(d) { return d.feature.properties[categoryField]; })
+ .rollup(function(leaves) {
+ return aggregationFunc(leaves, function(d) { return d.feature.properties[valueField]; });
+ })
+ .entries(data);
+ }
+
+ // Aggregation functions
+ const aggregationFunctions = {
+ sum: (leaves, accessor) => d3.sum(leaves, accessor),
+ max: (leaves, accessor) => d3.max(leaves, accessor),
+ min: (leaves, accessor) => d3.min(leaves, accessor),
+ mean: (leaves, accessor) => d3.mean(leaves, accessor),
+ median: (leaves, accessor) => d3.median(leaves, accessor),
+ //mode: (leaves, accessor) => d3.mode(leaves, accessor),
+ //cumsum: (leaves, accessor) => d3.cumsum(leaves, accessor),
+ //least: (leaves, accessor) => d3.least(leaves, accessor),
+ //variance: (leaves, accessor) => d3.variance(leaves, accessor),
+ //deviation: (leaves, accessor) => d3.deviation(leaves, accessor),
+ };
+
+ // Aggregate Data based on the category `categoryField` and the value `valueField`
+ var data = aggregateData(children, categoryField, aggregationFunctions[aggregation], valueField);
+
+ // Calculate full aggregation for centered Text
+ var totalAggregation = aggregationFunctions[aggregation](children, function(d) {
+ return d.feature.properties[valueField];
+ });
+ totalAggregation = totalAggregation.toFixed(digits);
+
+ // Make Chart
+ html = bakeTheStatsChart({
+ data: data,
+ valueFunc: function(d) { return d.values; },
+ outerRadius: r,
+ innerRadius: r - innerRadius,
+ totalAggregation: totalAggregation,
+ bubbleClass: 'cluster-bubble',
+ bubbleLabelClass: 'clustermarker-cluster-bubble-label',
+ pathClassFunc: function(d) {
+ return "category-" + d.data.key;
+ },
+ pathTitleFunc: function(d) {
+ return d.data.key + ' (' + d.value + ')';
+ }
+ });
+
+ } else {
+ var data = d3.nest() //Build a dataset for the pie chart
+ .key(function(d) { return d.feature.properties[categoryField]; })
+ .entries(children, d3.map)
+
+ if (type == "pie") {
+ html = bakeThePie({
+ data: data,
+ valueFunc: function(d) { return d.values.length; },
+ outerRadius: r,
+ innerRadius: r - innerRadius,
+ pieLabel: n,
+ pieClass: 'cluster-pie',
+ pieLabelClass: 'clustermarker-cluster-pie-label',
+ pathClassFunc: function(d) {
+ return "category-" + d.data.key;
+ },
+ pathTitleFunc: function(d) {
+ return d.data.key + ' (' + d.data.values.length + ')';
+ }
+ })
+ } else if (type == "horizontal") {
+ html = bakeTheBarChartHorizontal({
+ data: data,
+ width: options.width ? options.width : 70,
+ height: options.height ? options.height : 40,
+ barLabel: n,
+ barClass: 'cluster-bar',
+ barLabelClass: 'clustermarker-cluster-bar-label',
+ pathClassFunc: function(d) {
+ return "category-" + d.key;
+ },
+ pathTitleFunc: function(d) {
+ return d.key + ' (' + d.values.length + ')';
+ }
+ });
+ } else {
+ html = bakeTheBarChart({
+ data: data,
+ width: options.width ? options.width : 70,
+ height: options.height ? options.height : 40,
+ barLabel: n,
+ barClass: 'cluster-bar',
+ barLabelClass: 'clustermarker-cluster-bar-label',
+ pathClassFunc: function(d) {
+ return "category-" + d.key;
+ },
+ pathTitleFunc: function(d) {
+ return d.key + ' (' + d.values.length + ')';
+ }
+ });
+ }
+ }
+
+ //Create a new divIcon and assign the svg markup to the html property
+ var myIcon = new L.DivIcon({
+ html: html,
+ className: 'clustermarker-cluster',
+ iconSize: new L.Point(iconDim, iconDim)
+ });
+
+ return myIcon;
+ }
+ //function that generates a svg markup for a Statistics chart
+ function bakeTheStatsChart(options) {
+ if (!options.data || !options.valueFunc) {
+ return '';
+ }
+ var data = options.data,
+ valueFunc = options.valueFunc,
+ r = options.outerRadius,
+ rInner = options.innerRadius,
+ pathClassFunc = options.pathClassFunc,
+ pathTitleFunc = options.pathTitleFunc,
+ totalAggregation = options.totalAggregation,
+ bubbleLabelClass = options.bubbleLabelClass,
+ origo = (r + strokeWidth),
+ w = origo * 2,
+ h = w,
+ donut = d3.layout.pie(),
+ arc = d3.svg.arc().innerRadius(rInner).outerRadius(r);
+
+ let radius = w;
+
+ let pie = donut
+ .padAngle(1 / radius)
+ //.sort(null)
+ .value(valueFunc);
+
+ var arc = d3.svg.arc()
+ .innerRadius(rInner)
+ .outerRadius(r);
+
+ // Create SVG
+ var svg = document.createElementNS(d3.ns.prefix.svg, 'svg');
+ var vis = d3.select(svg)
+ .attr("width", w)
+ .attr("height", h)
+
+ // Create the Arcs
+ var arcs = vis.selectAll('g.arc')
+ .data(pie(data))
+ .enter().append('svg:g')
+ .attr('class', 'arc')
+ .attr('transform', 'translate(' + origo + ',' + origo + ')');
+
+ arcs.append('svg:path')
+ .attr('class', pathClassFunc)
+ .attr('stroke-width', strokeWidth)
+ .attr('d', arc)
+
+ // Display the text of each Arc
+ arcs.append('text')
+ .attr('transform', function(d) {
+ return 'translate(' + arc.centroid(d) + ')';
+ })
+ .attr('class', bubbleLabelClass)
+ .attr('text-anchor', 'middle')
+ .attr('fill', labelColor)
+ .attr('dy','.3em')
+ .text(function(d){ return d.data.values.toFixed(digits); })
+ .append('svg:title')
+ .text(pathTitleFunc);
+
+ // Show Label Background
+ if (labelBackground && labelBackground == true) {
+ vis.append('circle')
+ .attr('r', rInner-5)
+ .attr('transform', 'translate(' + origo + ',' + origo + ')')
+ .attr('fill', labelFill)
+ .attr('stroke', labelStroke)
+ .attr('stroke-width', strokeWidth)
+ .attr('opacity', labelOpacity)
+ }
+
+ // Display the total aggregation in the Center
+ vis.append('text')
+ .attr('x', origo)
+ .attr('y', origo)
+ .attr('class', bubbleLabelClass)
+ .attr('text-anchor', 'middle')
+ .attr('fill', labelColor)
+ .attr('stroke', labelStroke)
+ .attr('opacity', labelOpacity)
+ .attr('stroke-width', strokeWidth)
+ .attr('dy', '.3em')
+ .text(totalAggregation);
+
+ return serializeXmlNode(svg);
+ }
+ //function that generates a svg markup for a Pie chart
+ function bakeThePie(options) {
+ if (!options.data || !options.valueFunc) {
+ return '';
+ }
+ var data = options.data,
+ valueFunc = options.valueFunc,
+ r = options.outerRadius,
+ rInner = options.innerRadius,
+ pathClassFunc = options.pathClassFunc,
+ pathTitleFunc = options.pathTitleFunc,
+ pieClass = options.pieClass,
+ pieLabel = options.pieLabel ? options.pieLabel : d3.sum(data, valueFunc),
+ pieLabelClass = options.pieLabelClass,
+ origo = (r + strokeWidth),
+ w = origo * 2,
+ h = w,
+ donut = d3.layout.pie(),
+ arc = d3.svg.arc().innerRadius(rInner).outerRadius(r);
+
+ // Create SVG
+ var svg = document.createElementNS(d3.ns.prefix.svg, 'svg');
+ var vis = d3.select(svg)
+ .data([data])
+ .attr('class', pieClass)
+ .attr('width', w)
+ .attr('height', h);
+
+ // Create the Arcs
+ var arcs = vis.selectAll('g.arc')
+ .data(donut.value(valueFunc))
+ .enter().append('svg:g')
+ .attr('class', 'arc')
+ .attr('transform', 'translate(' + origo + ',' + origo + ')');
+
+ arcs.append('svg:path')
+ .attr('class', pathClassFunc)
+ .attr('stroke-width', strokeWidth)
+ .attr('d', arc)
+ .append('svg:title')
+ .text(pathTitleFunc);
+
+ // Create Title for Individual Elements and All in Cluster
+ pathTitleFunc = function(d) {
+ return d.key + ' (' + d.values.length + ')';
+ }
+ let allTitles = ""
+ if (sortTitlebyCount) {
+ allTitles = data
+ .sort(function(a, b) { return b.values.length - a.values.length; }) // Sort by length in descending order
+ .map(function(d) { return pathTitleFunc(d); })
+ .join('\n');
+ } else {
+ let categoryOrder = {};
+ for (var key in categoryMap) {
+ categoryOrder[categoryMap[key]] = parseInt(key);
+ }
+ allTitles = data
+ .sort(function(a, b) {
+ // Get the order values from categoryOrder
+ var orderA = categoryOrder[a.key] || Infinity;
+ var orderB = categoryOrder[b.key] || Infinity;
+ return orderA - orderB; // Sort in ascending order
+ })
+ .map(function(d) { return pathTitleFunc(d); })
+ .join('\n');
+ }
+
+ // Show Label Background
+ if (labelBackground && labelBackground == true) {
+ vis.append('circle')
+ .attr('r', rInner-5)
+ .attr('transform', 'translate(' + origo + ',' + origo + ')')
+ .attr('fill', labelFill)
+ .attr('stroke', labelStroke)
+ .attr('stroke-width', strokeWidth)
+ .attr('opacity', labelOpacity)
+ .append('svg:title')
+ .text(allTitles);
+ }
+
+ // Text
+ vis.append('text')
+ .attr('x',origo)
+ .attr('y',origo)
+ .attr('class', pieLabelClass)
+ .attr('text-anchor', 'middle')
+ .attr('fill', labelColor)
+ .attr('stroke', labelStroke)
+ .attr('opacity', labelOpacity)
+ .attr('stroke-width', strokeWidth)
+ .attr('dy','.3em')
+ .text(pieLabel)
+ .append('svg:title')
+ .text(allTitles);
+
+ //Return the svg-markup rather than the actual element
+ return serializeXmlNode(svg);
+ }
+ //function that generates a svg markup for a Bar chart
+ function bakeTheBarChart(options) {
+ if (!options.data) {
+ return '';
+ }
+ var data = options.data,
+ barClass = options.barClass,
+ barLabel = options.barLabel ? options.barLabel : d3.sum(data, function(d) { return d.values.length; }),
+ barLabelClass = options.barLabelClass,
+ width = options.width,
+ height = options.height,
+ pathClassFunc = options.pathClassFunc,
+ pathTitleFunc = options.pathTitleFunc,
+ x = d3.scale.ordinal().rangeRoundBands([0, width], 0.1),
+ y = d3.scale.linear().range([height, 0]);
+
+ x.domain(data.map(function(d) { return d.key; }));
+ y.domain([0, d3.max(data, function(d) { return d.values.length; })]);
+
+ // Create svg
+ var svg = document.createElementNS(d3.ns.prefix.svg, "svg");
+ var vis = d3.select(svg)
+ .attr('class', barClass)
+ .attr('width', width + strokeWidth)
+ .attr('height', height + 20 + strokeWidth);
+
+ // Create Bars
+ vis.selectAll('.bar')
+ .data(data)
+ .enter().append('rect')
+ .attr('class', pathClassFunc)
+ .attr('x', function(d) { return x(d.key); })
+ .attr('width', x.rangeBand())
+ .attr('y', function(d) { return y(d.values.length); })
+ .attr('height', function(d) { return height - y(d.values.length); })
+ .append('svg:title')
+ .text(pathTitleFunc);
+
+ // Create Title for Individual Elements and All in Cluster
+ let allTitles = ""
+ if (sortTitlebyCount) {
+ allTitles = data
+ .sort(function(a, b) { return b.values.length - a.values.length; }) // Sort by length in descending order
+ .map(function(d) { return pathTitleFunc(d); })
+ .join('\n');
+ } else {
+ let categoryOrder = {};
+ for (var key in categoryMap) {
+ categoryOrder[categoryMap[key]] = parseInt(key);
+ }
+ allTitles = data
+ .sort(function(a, b) {
+ // Get the order values from categoryOrder
+ var orderA = categoryOrder[a.key] || Infinity;
+ var orderB = categoryOrder[b.key] || Infinity;
+ return orderA - orderB; // Sort in ascending order
+ })
+ .map(function(d) { return pathTitleFunc(d); })
+ .join('\n');
+ }
+
+ // Bar Label Background
+ if (labelBackground && labelBackground == true) {
+ vis.append('rect')
+ .attr('x', (width - (width - 10)) / 2)
+ .attr('y', height + 5)
+ .attr('width', width - 10)
+ .attr('height', 15)
+ .attr('fill', labelFill)
+ .attr('stroke', labelStroke)
+ .attr('opacity', labelOpacity)
+ .attr('stroke-width', strokeWidth)
+ .append('svg:title')
+ .text(allTitles);
+ }
+
+ // Bar Label (below)
+ vis.append('text')
+ .attr('x', width / 2)
+ .attr('y', height + 13)
+ .attr('class', barLabelClass)
+ .attr('text-anchor', 'middle')
+ .attr('fill', labelColor)
+ .attr('stroke', labelStroke)
+ .attr('opacity', labelOpacity)
+ .attr('stroke-width', strokeWidth)
+ .attr('dy', '.3em')
+ .text(barLabel)
+ .append('svg:title')
+ .text(allTitles);
+
+ return serializeXmlNode(svg);
+ }
+ //function that generates a svg markup for a Bar chart (horizontal)
+ function bakeTheBarChartHorizontal(options) {
+ if (!options.data) {
+ return '';
+ }
+ var data = options.data,
+ barClass = options.barClass,
+ barLabel = options.barLabel ? options.barLabel : d3.sum(data, function(d) { return d.values.length; }),
+ barLabelClass = options.barLabelClass,
+ width = options.width,
+ height = options.height,
+ pathClassFunc = options.pathClassFunc,
+ pathTitleFunc = options.pathTitleFunc,
+ x = d3.scale.linear().range([0, width]),
+ y = d3.scale.ordinal().rangeRoundBands([0, height], 0.1);
+
+ x.domain([0, d3.max(data, function(d) { return d.values.length; })]);
+ y.domain(data.map(function(d) { return d.key; }));
+
+ // Create SVG
+ var svg = document.createElementNS(d3.ns.prefix.svg, "svg");
+ var vis = d3.select(svg)
+ .attr('class', barClass)
+ .attr('width', width + strokeWidth)
+ .attr('height', height + 20 + strokeWidth);
+
+ // Create Bars
+ vis.selectAll('.bar')
+ .data(data)
+ .enter().append('rect')
+ .attr('class', pathClassFunc)
+ .attr('x', 0)
+ .attr('y', function(d) { return y(d.key); })
+ .attr('width', function(d) { return x(d.values.length); })
+ .attr('height', y.rangeBand())
+ .append('svg:title')
+ .text(pathTitleFunc);
+
+ // Create Title for Individual Elements and All in Cluster
+ let allTitles = ""
+ if (sortTitlebyCount) {
+ allTitles = data
+ .sort(function(a, b) { return b.values.length - a.values.length; }) // Sort by length in descending order
+ .map(function(d) { return pathTitleFunc(d); })
+ .join('\n');
+ } else {
+ let categoryOrder = {};
+ for (var key in categoryMap) {
+ categoryOrder[categoryMap[key]] = parseInt(key);
+ }
+ allTitles = data
+ .sort(function(a, b) {
+ // Get the order values from categoryOrder
+ var orderA = categoryOrder[a.key] || Infinity;
+ var orderB = categoryOrder[b.key] || Infinity;
+ return orderA - orderB; // Sort in ascending order
+ })
+ .map(function(d) { return pathTitleFunc(d); })
+ .join('\n');
+ }
+
+ // Bar Label Background
+ if (labelBackground && labelBackground == true) {
+ vis.append('rect')
+ .attr('x', 0)
+ .attr('y', height + 3)
+ .attr('width', width)
+ .attr('height', 15)
+ .attr('fill', labelFill)
+ .attr('stroke', labelStroke)
+ .attr('opacity', labelOpacity)
+ .attr('stroke-width', strokeWidth)
+ .append('svg:title')
+ .text(allTitles);
+ }
+
+ // Bar Label
+ vis.append('text')
+ .attr('x', width / 2)
+ .attr('y', (height + 8))
+ .attr('class', barLabelClass)
+ .attr('text-anchor', 'middle')
+ .attr('dominant-baseline', 'middle')
+ .attr('alignment-baseline', 'middle')
+ .attr('stroke', labelStroke)
+ .attr('opacity', labelOpacity)
+ .attr('stroke-width', strokeWidth)
+ .attr('dy', '.3em')
+ .attr('fill', labelColor)
+ .text(barLabel)
+ .append('svg:title')
+ .text(allTitles);
+
+ return serializeXmlNode(svg);
+ }
+ //Helper function
+ function serializeXmlNode(xmlNode) {
+ if (typeof window.XMLSerializer != "undefined") {
+ return (new window.XMLSerializer()).serializeToString(xmlNode);
+ } else if (typeof xmlNode.xml != "undefined") {
+ return xmlNode.xml;
+ }
+ return "";
+ }
+ //Function for generating a legend with the same categories as in the clusterPie
+ function renderLegend() {
+ var data = Object.entries(categoryMap).map(([key, value]) => ({
+ key: key,
+ value: value
+ }));
+ var legendControl = L.control({position: legendOptions.position});
+
+ legendControl.onAdd = function(map) {
+ var div = L.DomUtil.create('div', 'clusterlegend');
+ div.innerHTML = legendOptions.title ? ' ';
+ }
+ });
+ popupContent += '' + label + ': ' + val + '