lgaChart36 = {
const districts = mpdDistrictsGeoJSON.features.map(d => d.properties.DISTRICT);
const colorScale = d3.scaleOrdinal(d3.schemeAccent).domain(districts);
const svg = d3
.create("svg")
.attr("width", width)
.attr("height", height)
.style("background", "#2D2537");
d3.selectAll("div.tooltip").remove();
const g = svg
.append("g")
const tooltip = d3
.select("body")
.append("div")
.attr("class", `tooltip ${classes}`)
.style("position", "absolute")
.style("opacity", 0)
.text("I'm a circle!");
// Create a tooltip div that is hidden by default
const tooltip2 = d3.select("body")
.append("div")
.attr("class", "tooltip")
.style("position", "absolute")
.style("visibility", "hidden")
.style("background-color", "white")
.style("border", "solid")
.style("border-width", "1px")
.style("border-radius", "5px")
.style("padding", "10px")
.style("box-shadow", "0px 0px 6px rgba(0, 0, 0, 0.3)");
// Append a rectangle to serve as the background
svg.append("rect")
.attr("width", "100%")
.attr("height", "100%")
.attr("fill", "lightgrey"); // Set the background color to lightgrey
// Append empty placeholder 'g' elements to the SVG for different layers
let districtOutlines = svg.append("g");
let shotSpotterAlerts = svg.append("g");
let bubbles = svg.append("g");
// Define the map projection to fit the GeoJSON data within the SVG dimensions
var projection = d3.geoMercator()
.fitExtent([[20, 20], [width, height]], mpdDistrictsGeoJSON);
// Define the path generator using the projection
var path = d3.geoPath()
.projection(projection);
// Calculate the total number of ShotSpotter alerts
const totalAlerts = ShotSpotterDataGeoJSON.features.length;
// Calculate the percentage of alerts for each district
const alertPercentages = mpd_geojson_with_race.features.map(district => {
const districtAlerts = ShotSpotterCleaned2_Source.filter(alert =>
alert.properties.SOURCE === district.properties.DISTRICT
).length;
return {
district: district.properties.DISTRICT,
ratio: district.properties.ratio,
percentage: (districtAlerts / totalAlerts) * 100
};
});
// Create a scale for bubble sizes to represent alert percentages
const minPercentage = d3.min(alertPercentages, d => d.percentage);
const maxPercentage = d3.max(alertPercentages, d => d.percentage);
// Adjust the domain to avoid zero-sized bubbles
const sizeScale = d3.scaleSqrt()
//format to only 2 decimal places
.domain([minPercentage, maxPercentage]) // Ensure minimum bubble size
.range([1, 100]); // Adjust range for better visibility
// Format for percentage display in tooltip
const formatPercent = d3.format(".2f");
// Draw district outlines on the map with tooltips
districtOutlines.selectAll('path')
.data(mpd_geojson_with_race.features)
.enter().append('path')
.attr('d', path)
.attr("fill", "white")
.style('stroke', "lightgrey")
.style('stroke-width', 3)
.on("mouseover", function(event, d) {
// Find the corresponding alert percentage data
const alertData = alertPercentages.find(a => a.district === d.properties.DISTRICT);
tooltip
.style("visibility", "visible")
.html(`
<strong>District:</strong> ${d.properties.DISTRICT}<br>
<strong>POC Ratio:</strong> ${(d.properties.ratio * 100).toFixed(2)}%<br>
<strong>Alerts:</strong> ${formatPercent(alertData.percentage)}% of total
`);
})
.on("mousemove", function(event) {
tooltip
.style("top", (event.pageY - 10) + "px")
.style("left", (event.pageX + 10) + "px");
})
.on("mouseout", function() {
tooltip.style("visibility", "hidden");
});
// Draw bubbles on the map to represent alert percentages
bubbles.selectAll("circle")
.data(alertPercentages)
.enter().append("circle")
.attr("cx", d => {
const district = mpd_geojson_with_race.features.find(f => f.properties.DISTRICT === d.district);
return path.centroid(district)[0];
})
.attr("cy", d => {
const district = mpd_geojson_with_race.features.find(f => f.properties.DISTRICT === d.district);
return path.centroid(district)[1];
})
.attr("r", d => sizeScale(d.percentage))
.style('fill', d => colorFunction(d.ratio))
.style("opacity", 0.8)
.style("stroke", "lightgrey")
.style('stroke-width', 3)
bubbles
.on("mouseover", (_evt, d) => {
tooltip
.style("opacity", 1)
.html(`x = ${d.ratio}<br>`)
.style("border-color", colorFunction(d.ratio));
})
.on("mousemove", (evt, d) => {
tooltip
.style("top", evt.pageY - 10 + "px")
.style("left", evt.pageX + 10 + "px");
})
.on("mouseout", () => {
tooltip.style("opacity", 0);
});
// Return the SVG node for further manipulation or rendering
return svg.node();
}