html`
<div style="font-size: 30px; font-weight: 700; margin: 20px 0 20px 0;">${viewof button1} <div style="margin-left: 30px;">What is ESG?</div> </div>
`
What is ESG?
viewof button1 = form(
html`
<form style="font-size: 16px; margin-bottom: -60px;">
<label class="first-button" style="margin-bottom: 0;">
<input id="first-radio-button" type="radio" name="optradio" value ="0" checked>
△
</label>
<span class="brmedium"></span>
<label class="second-button" style="margin-top: 0;">
<input id="second-radio-button" type="radio" name="optradio" value="1">
▽
</label>
</form>
`
)
button1Up = {
if (button1.optradio == '0') {
return "green"
} else {
return "#d3d3d3"
}
};
button1Down = {
if (button1.optradio == '1') {
return "green"
} else {
return "#d3d3d3"
}
};
button1Text = {
if (button1.optradio == '0') {
return "display: none";
} else {
return " ";
}
};
tableData = [
{name: "Examples",
e: "<ul><li>Carbon emissions</li><li>Water pollution</li><li>Plastic packaging</li></ul>",
s: "<ul><li>Lobbying</li><li>Labor relations</li><li>Supply chain</li></ul>",
g: "<ul><li>Board diversity</li><li>Shareholder rights</li>",}
];
html`
<div style="${button1Text}">
ESG generally refers to the focus on <a target="_blank" href="https://reason.org/topics/pension-reform/esg/">environmental, social, and governance</a> issues that are considered by investment managers, financial and banking firms, and activist interests when they are measuring risks and opportunities.
<br><br>
When paired with standard financial forecasting, the ESG lens can help money managers find risks and opportunities not accounted for in an asset’s market price. However, when ESG is used to push capital towards or away from assets according to political preferences that are independent of financial considerations, ESG investments can become a type of political contribution.
<br><br>
Credit rating agencies and other firms continuously develop new <a target="_blank" href="https://www.investopedia.com/terms/e/environmental-social-and-governance-esg-criteria.asp">ESG ratings</a> that attempt to financially assess the societal impact of public corporations across these three—environmental, social and governance—factors. These ESG ratings and other ESG-centered investing and risk mitigation strategies have gained significant traction among major asset managers and institutional investors looking to maintain investment revenue.
<br><br>
Common ESG areas of focus currently include:
<br><br>
<div class="data-table">
<table>
<tr>
<th style="font-size: 35px;">E</th>
<th style="font-size: 35px;">S</th>
<th style="font-size: 35px;">G</th>
</tr>
<tr>
<th style="font-size: 20px;">Environmental</th>
<th style="font-size: 20px;">Social</th>
<th style="font-size: 20px;">Governance</th>
</tr>
${tableData.map((d, i) => html`
<tr>
<td style="text-align: left; background-color: ${i % 2 == 0 ? "#f5f5f5" : "#fff"};">${d.e}</td>
<td style="text-align: left; background-color: ${i % 2 == 0 ? "#f5f5f5" : "#fff"};">${d.s}</td>
<td style="text-align: left; background-color: ${i % 2 == 0 ? "#f5f5f5" : "#fff"};">${d.g}</td>
</tr>
`)}
</table>
</div>
The proliferation of ESG activism is generating significant controversy in many states because of its potential implications on market capitalism and how it may impact prospects in many industry sectors. In some cases, activist investors attempting to shape society and the economy according to their own aspirations look to create large pools of private and public funds to target companies they may deem detrimental to their ideal society. Energy companies, agricultural firms, tobacco companies, gun manufacturers, correctional service providers, and other industries have been among the targets. There is a growing and legitimate concern among many public officials that activists are using ESG-related risk mitigation rhetoric and branding to raise money from state treasury funds, public pension funds, and other public trusts for political use, which runs counter to longstanding fiduciary norms.
<br><br>
Contextualizing ESG policies is inherently complicated. For example, is ESG simply investors considering the genuine financial risks associated with a company’s actions on environmental or social issues, or is it a recklessly so-called ‘woke’ use of capital to push political goals?
<br><br>
A public pension fund considering sea level rise when weighing an investment in waterfront commercial real estate is prudent investing. Similarly, ensuring that a company’s labor force is content and that supply chains are sustainable is sound management. By contrast, using taxpayer money to allocate resources to companies and projects based on political ideology crosses the line into activism and violates fiduciary standards requiring a singular focus on maximizing market returns to ensure that public pension benefits will be fully funded, for example.
<br><br>
While taking many forms, the ESG activism can be broadly described as <a target="_blank" href="https://reason.com/volokh/2022/06/02/may-public-pension-plan-managers-use-environmental-social-and-governance-investment-practices/">stakeholder capitalism</a>—a modern form of <a target="_blank" href="https://reason.com/2022/10/15/the-rise-of-the-stake-holders/">corporatism</a>. Different from shareholder capitalism—where the goals and sole considerations are focused on returns to the owner of an investment—stakeholder capitalism is based on amorphic political and ideological objectives.
<br><br>
Increasingly, activist entities within traditional financial services organizations like banks, fund managers, and institutional investors are using the hundreds of billions of dollars in taxpayer money they manage to promote stakeholder capitalism ideology. This uses public resources to promote the activists’ political goals and distorts the free market.
<br><br>
BlackRock Chairman and CEO Larry Fink outlined his view in his 2022 <a target="_blank" href="https://www.blackrock.com/corporate/investor-relations/larry-fink-ceo-letter">letter</a> to CEOs of the companies BlackRock’s clients invest in: <span style="font-style: italic;">“Stakeholder capitalism is not about politics. It is not a social or ideological agenda. It is not ‘woke.’ It is capitalism, driven by mutually beneficial relationships between you and the employees, customers, suppliers, and communities your company relies on to prosper."</span>
<br><br>
Mr. Fink’s suggestion that this activism is based on universally-shared interests is questionable at best. ESG policies often take sides on some of our time’s most contentious political issues, including environmental and energy policy, foreign policy, abortion, and guns, to name just a few. ESG activists often attempt to move these types of issues from the democratic and judicial processes to private financial markets and corporate boards, politicizing what have, in many cases, historically been non-political organizations and institutions.
<br><br>
</div>
<hr style='margin:2px; padding:0; border-top: 3px solid green;'>
`
html`
<div style="diplay: inline-block; font-size: 30px; font-weight: 700; margin: 20px 0 20px 0;">${viewof button2} <div style="margin-left: 30px;">How does ESG enter public finance?</div> </div>
`
viewof button2 = form(
html`
<form style="font-size: 16px; margin-bottom: -60px;">
<label class="third-button" style="margin-bottom: 0;">
<input id="third-radio-button" type="radio" name="optradio" value ="0" checked>
△
</label>
<span class="brmedium"></span>
<label class="fourth-button" style="margin-top: 0;">
<input id="fourth-radio-button" type="radio" name="optradio" value ="1">
▽
</label>
</form>
`
)
button2Up = {
if (button2.optradio == '0') {
return "green"
} else {
return "#d3d3d3"
}
};
button2Down = {
if (button2.optradio == '1') {
return "green"
} else {
return "#d3d3d3"
}
};
button2Text = {
if (button2.optradio == '0') {
return "display: none";
} else {
return " ";
}
};
html`
<div style="${button2Text}">
The largest pools of state-managed capital are usually public pension funds and trusts. Large state pension plans and treasuries are essentially large institutional investors in the market, similar to a university endowment or insurance fund. These trusts are operated by boards of trustees with their own personal perspectives and biases on ESG issues. In the government context, there is a long history of politicians encouraging or even directly mandating particular types of corporate investment or divestment in states like California, New York, and Texas.
<br><br>
Before the year 2000, multi-billion dollar public pension funds historically relied on revenue generated by traditional, low-risk assets such as government bonds and blue-chip stocks that paid investment returns and dividends that kept pension plans’ costs low while they funded constitutionally-protected retirement benefits of public workers. However, as capital market outlooks diminished in the wake of a historic drop in global interest rates, multiple recessions, and growing stock market volatility, public pension plans were slow to adjust their investment return assumptions downward (see figure below), contributing to the more than $1 trillion in unfunded public pension liabilities today and growing pressure on public pension fund managers to pursue higher investment returns.
<br><br>
<span style="font-weight: 700;">Treasury Yields vs Assumed Rates of Return</span> <div style="height: 10px;"></div>
<div style="font-size: 14px;
margin: -5px 0 10px 0;
font-family: 'Open Sans', sans-serif;
text-align: center;">
<div style="margin-left: 10px;
font-size: 14px;
border-radius: 50%;
display: inline-block;
width: 10px;
height: 10px; background-color: #f63;"></div>
State & Local Assumed Rate of Return
<div style="margin-left: 10px;
font-size: 14px;
border-radius: 50%;
display: inline-block;
width: 10px;
height: 10px;background-color: #2879cb;"></div>
30-Year Treasury Yield
</div>
<div style="content:''; display: table; clear: both; width: 100%;">
<div style="width: 100%; font-family: 'Open Sans', sans-serif;">
${arrTreasuryChart}
</div>
</div>
<br><br>
For many <a target="_blank" href="https://reason.org/topics/pension-reform/">U.S. public pension systems</a>, the large gap between their overly optimistic <a target="_blank" href="https://reason.org/data-visualization/2022-investment-results-for-state-pension-plans/">investment return assumptions</a> and risk-free bond yields has created growing pressure on pension fund managers to take on more risks via actively managed alternative investments, such as private equity, hedge funds, private real estate, private debt, natural resources, and other non-traditional assets. The figure below shows that alternative investments among all U.S. public pension funds have increased from 9% in 2001 to 29% in 2021.
<br><br>
<span style="font-weight: 700;">Average Asset Allocation within U.S. Public Pension Investment Portfolios</span> <div style="height: 10px;"></div>
${assetAllocationChart}
Many of these alternative investments are not transparent, carry additional financial risks and incur significant fees from outside money managers.
<br><br>
Notably, the private and opaque nature of alternative investment valuation and reporting also creates an opportunity for politicizing public pension fund investments without the approval of the pension system’s trustees.
<br><br>
</div>
<hr style='margin:2px; padding:0; border-top: 3px solid green;'>
`
html`
<div style="diplay: inline-block; font-size: 30px; font-weight: 700; margin: 20px 0 20px 0;">${viewof button3} <div style="margin-left: 30px;">Who are the major players?</div> </div>
`
viewof button3 = form(
html`
<form style="font-size: 16px; margin-bottom: -60px;">
<label class="fifth-button" style="margin-bottom: 0;">
<input id="fifth-radio-button" type="radio" name="optradio" value ="0" checked>
△
</label>
<span class="brmedium"></span>
<label class="sixth-button" style="margin-top: 0;">
<input id="sixth-radio-button" type="radio" name="optradio" value ="1">
▽
</label>
</form>
`
)
button3Up = {
if (button3.optradio == '0') {
return "green"
} else {
return "#d3d3d3"
}
};
button3Down = {
if (button3.optradio == '1') {
return "green"
} else {
return "#d3d3d3"
}
};
button3Text = {
if (button3.optradio == '0') {
return "display: none";
} else {
return " ";
}
};
html`
<div style="${button3Text}">
BlackRock, State Street Corporation, The Vanguard Group, AllianceBernstein, Nuveen, and PIMCO are some of the largest U.S.-based financial managers involved in marketing and executing ESG-related investment strategies. The American Federation of Labor and Congress of Industrial Organizations (AFL-CIO) and large public sector labor unions, such as the American Federation of Teachers, also push their members who serve as trustees to have those organization investments adhere to environmental, social and governance standards. This is in sharp conflict with pension plans’ duty to make fiduciary decisions in the sole interest of maximizing investment returns to ensure that all of the pension benefits promised to and earned by members will be fully funded.
<br><br>
Additionally, there are groups like the <a target="_blank" href="https://www.ceres.org/networks/ceres-investor-network">Ceres Investor Network on Climate Risk and Sustainability</a>, which says it “includes more than 220 institutional investors managing more than $60 trillion in assets” as it works “with our members to advance sustainable investment practices, engage with corporate leaders, and advocate for key policy and regulatory solutions to accelerate the transition to a just, sustainable, net zero emissions economy.”
<br><br>
Another group is <a target="_blank" href="https://www.climateaction100.org">Climate Action 100+</a>, which says it “is an investor-led initiative to ensure the world’s largest corporate greenhouse gas emitters take necessary action on climate change.”
<br><br>
<a target="_blank" href="https://reason.org/commentary/mapping-public-pension-esg-investment-efforts/">The map</a> below identifies the public entities—state and local—that have signed on to Ceres and/or Climate Action 100+. You can click on individual states to see the public entities that have joined these groups.
<br>
<div style="width: 100%;">
<div style="width: 90%; margin-right: 5%; margin-left: 5%;">
${viewof currentState2}
</div>
<div style="width: 90%; margin-right: 5%; margin-left: 5%;">
${viewof info_box1}
</div>
</div>
<br><br>
</div>
<hr style='margin:2px; padding:0; border-top: 3px solid green;'>
`
html`
<div style="diplay: inline-block; font-size: 30px; font-weight: 700; margin: 20px 0 20px 0;">${viewof button4} <div style="margin-left: 30px;">Why is ESG and other activist investing a problem?</div> </div>
`
viewof button4 = form(
html`
<form style="font-size: 16px; margin-bottom: -60px;">
<label class="seventh-button" style="margin-bottom: 0;">
<input id="seventh-radio-button" type="radio" name="optradio" value ="0" checked>
△
</label>
<span class="brmedium"></span>
<label class="eighth-button" style="margin-top: 0;">
<input id="eighth-radio-button" type="radio" name="optradio" value ="1">
▽
</label>
</form>
`
)
button4Up = {
if (button4.optradio == '0') {
return "green"
} else {
return "#d3d3d3"
}
};
button4Down = {
if (button4.optradio == '1') {
return "green"
} else {
return "#d3d3d3"
}
};
button4Text = {
if (button4.optradio == '0') {
return "display: none";
} else {
return " ";
}
};
html`
<div style="${button4Text}">
Although ESG is often touted as a risk mitigation strategy by its proponents, ESG activism that prioritizes political objectives over maximizing investment returns enhances the risk of underperformance for public pension system investments. This risk ultimately falls on taxpayers, who would pay for public pension shortfalls through higher taxes and contributions to the system.
<br><br>
Activism in institutional investing also creates arbitrary standards that are easy to manipulate. ESG investing currently lacks universally agreed-upon standards, allowing activists to create their own performance benchmarks lacking consistency, objectivity, and accountability. Being the largest investment pools funded primarily with taxpayer money, public pension systems and trusts are particularly exposed to the risk associated with activist investing.
In <a target="_blank" href="https://www.wsj.com/articles/esg-does-neither-much-good-nor-very-well-evidence-composite-scores-impact-reports-strategy-jay-clayton-rating-agents-11663006833">The Wall Street Journal</a>, Terrence R. Keeley, a former Blackrock senior executive, wrote:
<br><br>
<hr style='margin:2px; padding:0; border-top: 3px solid #333;'>
<h4 style="border-radius: 5px; font-style: italic; background-color: #fff; color: #000; text-align: center; padding-bottom: 15px;">
“Trillions of dollars have poured into environmental, social and governance funds in recent years. In 2021 alone, the figure grew $8 billion a day. Bloomberg Intelligence projects more than one-third of all globally managed assets could carry explicit ESG labels by 2025, amounting to more than $50 trillion. Yet for a financial phenomenon this pervasive, there is astonishingly little evidence of its tangible benefit.”</h4>
<hr style='margin:2px; padding:0; border-top: 3px solid #333;'>
<br><br>
In examining the evidence for whether being socially responsible is creating value for companies and investors, Bradford Cornell, professor emeritus at the Anderson Graduate School of Management at the University of California-Los Angeles, and Aswath Damodaran, a professor at the Stern School of Business at New York University, <a target="_blank" href="https://papers.ssrn.com/sol3/papers.cfm?abstract_id=3557432">wrote</a> that “telling firms that being socially responsible will deliver higher growth, profits and value is false advertising.” The researchers concluded that “evidence that investors can generate positive excess returns with ESG-focused investing is weak.”
<br><br>
In a 2022 <a target="_blank" href="https://papers.ssrn.com/sol3/papers.cfm?abstract_id=4179647">working paper</a> for the Rock Center for Corporate Governance at Stanford University, other researchers conclude: "We find that while ESG ratings providers may convey important insights into the nonfinancial impact of companies, significant shortcomings exist in their objectives, methodologies, and incentives which detract from the informativeness of their assessments."
<br><br>
Private individuals and institutions are free to allocate their assets in whatever ways they see fit. Publicly sourced assets, however, are a different story. Public pension funds <a target="_bank" href="https://reason.org/commentary/public-retirement-plan-assets-should-never-be-utilized-for-political-purposes/">have a fiduciary duty</a> to manage assets in ways that ensure they have funding to provide all of the retirement benefits promised to their members. The subjective, non-pecuniary nature of ESG activism is inconsistent with these fiduciary responsibilities and the objectives of public sector retirement systems.
<br><br>
The over <a target="_blank" href="https://www.nasra.org/content.asp?admin=Y&contentid=200">$5 trillion</a> in state and local public pension assets is not something to be leveraged for political causes by plan participants or administrators. Unfortunately, rather than focusing on their responsibility to fully fund public pension plans, too many public entities are increasingly becoming activists on ESG and other political issues.
<br><br>
</div>
<hr style='margin:2px; padding:0; border-top: 3px solid green;'>
`
html`
<style>
#first-radio-button, #second-radio-button,
#third-radio-button, #fourth-radio-button,
#fifth-radio-button, #sixth-radio-button,
#seventh-radio-button, #eighth-radio-button {
display: none;
}
.first-button {
color: ${button1Up};
border: none;
padding: 0;
font-size: 25px;
font-weight: 800;
}
.second-button {
color: ${button1Down};
border-radius: 0;
padding: 0;
font-size: 25px;
}
.third-button {
color: ${button2Up};
border-radius: 0;
padding: 0;
font-size: 25px;
}
.fourth-button {
color: ${button2Down};
border-radius: 0;
padding: 0;
font-size: 25px;
}
.fifth-button {
color: ${button3Up};
border-radius: 0;
padding: 0;
font-size: 25px;
}
.sixth-button {
color: ${button3Down};
border-radius: 0;
padding: 0;
font-size: 25px;
}
.seventh-button {
color: ${button4Up};
border-radius: 0;
padding: 0;
font-size: 25px;
}
.eighth-button {
color: ${button4Down};
border-radius: 0;
padding: 0;
font-size: 25px;
}
.brmedium {
display: block;
margin-bottom: -0.4em;
}
</style>
`
format = ({
date1: d3.timeFormat("%Y"), // Timeline Tooltip Date Format
date2: d3.timeFormat("%Y - %B"), // Timeline Tooltip Date Format
date3: d3.timeFormat("%B %d, %Y"), // Timeline Tooltip Date Format
percent1d: d3.format(".1%"),
abs: d3.format(",.0f"),
comma: d3.format("$,.4s"),
avg: d3.format("(,.1f"),
billion: d3.format("$.4s"),
y_axis: d3.format("$.2s"),
text: d3.format("$,.0f")
})
tempAA = FileAttachment("./data/asset-allocation.csv").csv().then(
function(data) {
data.forEach(function(d) {
d.date = d3.timeParse("%Y")(d["Fiscal Year"]);
});
return data;
}
)
// tempAA;
tempAA2 = aq.from(tempAA)
.derive({"id": d => "United States"})
// .derive({"date": d => d["Fiscal Year"]})
.derive({"Equities": d => +d["Equities"] / 100})
.derive({"Fixed Income": d => +d["Fixed Income"] / 100})
.derive({"Private Equity": d => +d["Private Equity"] / 100})
.derive({"Real Estate": d => +d["Real Estate"] / 100})
.derive({"Cash": d => +d["Cash"] / 100})
.derive({"Other & Misc. Alternatives": d => (+d["Other"] + +d["Misc. Alternatives"]) / 100})
.derive({"Commodities": d => +d["Commodities"] / 100})
.derive({"Hedge Fund": d => +d["Hedge Fund"] / 100})
.derive({"first_value": d => +d["Cash"],
"second_value": d => +d["Fixed Income"] + +d["Cash"],
"third_value": d => +d["Fixed Income"] + +d["Cash"] + +d["Equities"],
"fourth_value": d => +d["Fixed Income"] + +d["Cash"] + +d["Equities"] + +d["Other & Misc. Alternatives"],
"fifth_value": d => +d["Fixed Income"] + +d["Cash"] + +d["Equities"] + +d["Other & Misc. Alternatives"] + +d["Commodities"],
"sixth_value": d => +d["Fixed Income"] + +d["Cash"] + +d["Equities"] + +d["Other & Misc. Alternatives"] + +d["Commodities"] + +d["Hedge Fund"],
"seventh_value": d => +d["Fixed Income"] + +d["Cash"] + +d["Equities"] + +d["Other & Misc. Alternatives"] + +d["Commodities"] + +d["Hedge Fund"] + +d["Real Estate"],
"eighth_value": d => +d["Fixed Income"] + +d["Cash"] + +d["Equities"] + +d["Other & Misc. Alternatives"] + +d["Commodities"] + +d["Hedge Fund"] + +d["Real Estate"] + +d["Private Equity"],
"first_layer_top": d => +d["Fixed Income"] + +d["Cash"],
"second_layer_top": d => +d["Fixed Income"] + +d["Cash"] + +d["Equities"],
"third_layer_top": d => +d["Fixed Income"] + +d["Cash"] + +d["Equities"] + +d["Other & Misc. Alternatives"],
"fourth_layer_top": d => +d["Fixed Income"] + +d["Cash"] + +d["Equities"] + +d["Other & Misc. Alternatives"] + +d["Commodities"],
"fifth_layer_top": d => +d["Fixed Income"] + +d["Cash"] + +d["Equities"] + +d["Other & Misc. Alternatives"] + +d["Commodities"] + +d["Hedge Fund"],
"sixth_layer_top": d => +d["Fixed Income"] + +d["Cash"] + +d["Equities"] + +d["Other & Misc. Alternatives"] + +d["Commodities"] + +d["Hedge Fund"] + +d["Real Estate"],
"seventh_layer_top": d => +d["Fixed Income"] + +d["Cash"] + +d["Equities"] + +d["Other & Misc. Alternatives"] + +d["Commodities"] + +d["Hedge Fund"] + +d["Real Estate"] + +d["Private Equity"]})
// .filter((d, $) => d.State == $.stateSelection)
.select("id", "date", "first_value", "second_value", "third_value", "fourth_value", "fifth_value", "sixth_value", "seventh_value", "eighth_value", "first_layer_top", "second_layer_top", "third_layer_top", "fourth_layer_top", "fifth_layer_top", "sixth_layer_top", "seventh_layer_top", "Equities", "Fixed Income", "Private Equity", "Real Estate", "Cash", "Other & Misc. Alternatives", "Commodities", "Hedge Fund")
//.orderby('State')
.objects();
dataAA = tempAA2;
dataDict2 = ({
"first_value": {
label: "Cash",
color: '#2166ace6',
lineColor: 'rgb(255, 255, 255, 0.85)'
},
"second_value": {
label: "Fixed Income",
color: "#4393c3e6",
lineColor: 'rgb(255, 255, 255, 0.85)'
},
"third_value": {
label: "Equities",
color: '#92c5dee6',
lineColor: 'rgb(255, 255, 255, 0.85)'
},
"fourth_value": {
label: "Other & Misc. Alternatives",
color: '#d1e5f0e6',
lineColor: 'rgb(255, 255, 255, 0.85)'
},
"fifth_value": {
label: "Commodities",
color: '#f7f7f7e6',
lineColor: 'rgb(255, 255, 255, 0.85)'
},
"sixth_value": {
label: "Hedge Fund",
color: '#fddbc7e6',
lineColor: 'rgb(255, 255, 255, 0.85)'
},
"seventh_value": {
label: "Real Estate",
color: '#f4a582e6',
lineColor: 'rgb(255, 255, 255, 0.85)'
},
"eighth_value": {
label: "Private Equity",
color: '#d6604de6',
lineColor: 'rgb(255, 255, 255, 0.85)'
},
})
arrTreasuryData = FileAttachment("./data/tyx_arr.csv").csv().then(
function(data) {
data.forEach(function(d) {
d.date = d3.timeParse("%m/%d/%y")(d.Date);
});
return data;
}
)
// arrTreasuryData;
arrTreasuryData2 = aq.from(arrTreasuryData)
.derive({"Treasury": d => +d["Treasury"] / 100})
.derive({"ARR": d => +d["ARR"] / 100})
.derive({"State": d => "United States"})
.select('date', 'Year', 'State', 'Treasury', 'ARR')
.objects();
aTData = arrTreasuryData2;
dataDict3 = ({
"Treasury": {
label: "Treasury",
color: "#d3d3d3",
lineColor: "#2879cb"
},
"ARR": {
label: "ARR",
color: "#d3d3d3",
lineColor: "#f63"
}
})
//-----------------------------Revenue Chart------------------------------------
assetAllocationChart = {
let height = 375;
let margin = {top: 50, right: 20, bottom: 50, left: 50}
let innerHeight = height - margin.top - margin.bottom;
let innerWidth = width - margin.left - margin.right;
let chartid = "asset-allocation-chart";
const selectGroup = "United States"; // array of the checkbox selections for filtering
const selectTypeAll = "first_layer_top";
const selectGroupAll = selectGroup;
let filteredAll = dataAA.filter(d => selectGroupAll.includes(d.id)); // filter data.set for extext
let extDataAll = d3.extent(filteredAll, d => d[selectTypeAll]); // filter data.set for extext
let selectLookupAll = d3.group(filteredAll, d => d.date.toISOString());
let selectDatesAll = Array.from(selectLookupAll).map(d => d[1][0].date);
// First Area
const selectTypeOne = "first_value";
let filteredOne = dataAA.filter(d => selectGroup.includes(d.id)); // filter data.set for extext
let extDataOne = d3.extent(filteredOne, d => d[selectTypeOne]); // filter data.set for extext
let extDatesOne = d3.extent(filteredOne, d => d.date);
let selectSeriesOne = d3.groups(filteredOne, d => d.id);
let selectLookupOne = d3.group(filteredOne, d => d.date.toISOString())
let selectDatesOne = Array.from(selectLookupOne).map(d => d[1][0].date);
// Second Area
const selectTypeTwo = "second_value";
const selectGroupTwo = selectGroup; // array of the checkbox selections for filtering
let filteredTwo = dataAA.filter(d => selectGroupTwo.includes(d.id)); // filter data.set for extext
let extDataTwo = d3.extent(filteredTwo, d => d[selectTypeTwo]); // filter data.set for extext
let extDatesTwo = d3.extent(filteredTwo, d => d.date);
let selectSeriesTwo = d3.groups(filteredTwo, d => d.id);
let selectLookupTwo = d3.group(filteredTwo, d => d.date.toISOString())
let selectDatesTwo = Array.from(selectLookupTwo).map(d => d[1][0].date);
// Third Area
const selectTypeThree = "third_value";
const selectGroupThree = selectGroup;
let filteredThree = dataAA.filter(d => selectGroupThree.includes(d.id));
let extDataThree = d3.extent(filteredThree, d => d[selectTypeThree]);
let extDatesThree = d3.extent(filteredThree, d => d.date);
let selectSeriesThree = d3.groups(filteredThree, d => d.id);
let selectLookupThree = d3.group(filteredThree, d => d.date.toISOString());
let selectDatesThree = Array.from(selectLookupThree).map(d => d[1][0].date);
// Fourth Area
const selectTypeFour = "fourth_value";
const selectGroupFour = selectGroup;
let filteredFour = dataAA.filter(d => selectGroupFour.includes(d.id));
let extDataFour = d3.extent(filteredFour, d => d[selectTypeFour]);
let extDatesFour = d3.extent(filteredFour, d => d.date);
let selectSeriesFour = d3.groups(filteredFour, d => d.id);
let selectLookupFour = d3.group(filteredFour, d => d.date.toISOString());
let selectDatesFour = Array.from(selectLookupFour).map(d => d[1][0].date);
// Fifth Area
const selectTypeFive = "fifth_value";
const selectGroupFive = selectGroup;
let filteredFive = dataAA.filter(d => selectGroupFive.includes(d.id));
let extDataFive = d3.extent(filteredFive, d => d[selectTypeFive]);
let extDatesFive = d3.extent(filteredFive, d => d.date);
let selectSeriesFive = d3.groups(filteredFive, d => d.id);
let selectLookupFive = d3.group(filteredFive, d => d.date.toISOString());
let selectDatesFive = Array.from(selectLookupFive).map(d => d[1][0].date);
// Sixth Area
const selectTypeSix = "sixth_value";
const selectGroupSix = selectGroup;
let filteredSix = dataAA.filter(d => selectGroupSix.includes(d.id));
let extDataSix = d3.extent(filteredSix, d => d[selectTypeSix]);
let extDatesSix = d3.extent(filteredSix, d => d.date);
let selectSeriesSix = d3.groups(filteredSix, d => d.id);
let selectLookupSix = d3.group(filteredSix, d => d.date.toISOString());
let selectDatesSix = Array.from(selectLookupSix).map(d => d[1][0].date);
// Seventh Area
const selectTypeSeven = "seventh_value";
const selectGroupSeven = selectGroup;
let filteredSeven = dataAA.filter(d => selectGroupSeven.includes(d.id));
let extDataSeven = d3.extent(filteredSeven, d => d[selectTypeSeven]);
let extDatesSeven = d3.extent(filteredSeven, d => d.date);
let selectSeriesSeven = d3.groups(filteredSeven, d => d.id);
let selectLookupSeven = d3.group(filteredSeven, d => d.date.toISOString());
let selectDatesSeven = Array.from(selectLookupSeven).map(d => d[1][0].date);
// Eighth Area
const selectTypeEight = "eighth_value";
const selectGroupEight = selectGroup;
let filteredEight = dataAA.filter(d => selectGroupEight.includes(d.id));
let extDataEight = d3.extent(filteredEight, d => d[selectTypeEight]);
let extDatesEight = d3.extent(filteredEight, d => d.date);
let selectSeriesEight = d3.groups(filteredEight, d => d.id);
let selectLookupEight = d3.group(filteredEight, d => d.date.toISOString());
let selectDatesEight = Array.from(selectLookupEight).map(d => d[1][0].date);
const svg = d3.create("svg")
.attr("viewBox", [0, 0, width, height])
.attr("id", chartid);
const group = svg.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
let x = d3.scaleTime()
.range([0, width - margin.left - margin.right])
.domain(extDatesOne);
console.log(extDatesOne)
let y = d3.scaleLinear()
.range([innerHeight, 0])
.domain([0, 1])
.nice();
let xAxis = d3.axisBottom(x);
let yAxis = d3.axisLeft(y);
// X-AXIS
if (width > 700) {
group.append("g")
.attr("class", "x axis")
.attr("transform", "translate("+ 0 + "," + innerHeight + ")")
.call(xAxis)
.call(d3.axisBottom(x)
.tickFormat(d3.timeFormat("%Y"))
.tickSize(5)
.ticks(10)
)
.call(g => g.selectAll(".domain").remove())
.style('color', 'rgb(129, 129, 129)')
.style("font-size", "12px")
.style('font-family', "'Open Sans', sans-serif")
.attr("stroke-opacity", 0.5);
} else {
group.append("g")
.attr("class", "x axis")
.attr("transform", "translate("+ 0 + "," + innerHeight + ")")
.call(xAxis)
.call(d3.axisBottom(x)
.tickFormat(d3.timeFormat("'%y"))
.tickSize(10)
)
.call(g => g.selectAll(".domain").remove())
.style('color', 'rgb(129, 129, 129)')
.style('font-family', "'Open Sans', sans-serif")
.attr("stroke-opacity", 0.5);
}
// Y-Axis
group.append("g")
.attr("class", "y axis")
.call(yAxis)
.call(d3.axisLeft(y)
.ticks(6)
.tickFormat((d) => d3.format(`.0%`)(d)))
.call(g => g.select(".domain").remove())
.call(g => g.selectAll(".tick line").clone()
.attr("x2", innerWidth + 50))
.call(g => g.selectAll(".tick text")
.style("font-size", "12px")
.style("font-weight", 400)
.attr("fill", "rgb(129, 129, 129)")
.attr("transform", `translate(0, -7)`))
.call(g => g.selectAll(".tick line")
.attr("transform", `translate(-40, 0)`)
.attr("stroke-opacity", 0.2))
.call(g => g.append("text")
.attr("text-anchor", "middle")
.style("font-size", "16px")
.attr("fill", "currentColor"))
.attr('font-family', "'Open Sans', sans-serif");
// RIGHT Y-AXIS
// Areas
let lineGroup = group.append("g")
.attr('class', 'line-group');
let areaOne = d3.area()
.defined(d => !isNaN(d[selectTypeOne]))
.x(d => x(d.date))
.y1(d => y(d[selectTypeOne]))
.y0(y(0));
let areaTwo = d3.area()
.defined(d => !isNaN(d[selectTypeTwo]))
.x(d => x(d.date))
.y1(d => y(d["first_layer_top"]))
.y0(d => y(d[selectTypeOne]));
let areaThree = d3.area()
.defined(d => !isNaN(d[selectTypeThree]))
.x(d => x(d.date))
.y1(d => y(d["second_layer_top"]))
.y0(d => y(d["first_layer_top"]));
let areaFour = d3.area()
.defined(d => !isNaN(d[selectTypeFour]))
.x(d => x(d.date))
.y1(d => y(d["third_layer_top"]))
.y0(d => y(d["second_layer_top"]));
let areaFive = d3.area()
.defined(d => !isNaN(d[selectTypeFive]))
.x(d => x(d.date))
.y1(d => y(d["fourth_layer_top"]))
.y0(d => y(d["third_layer_top"]));
let areaSix = d3.area()
.defined(d => !isNaN(d[selectTypeSix]))
.x(d => x(d.date))
.y1(d => y(d["fifth_layer_top"]))
.y0(d => y(d["fourth_layer_top"]));
let areaSeven = d3.area()
.defined(d => !isNaN(d[selectTypeSeven]))
.x(d => x(d.date))
.y1(d => y(d["sixth_layer_top"]))
.y0(d => y(d["fifth_layer_top"]));
let areaEight = d3.area()
.defined(d => !isNaN(d[selectTypeEight]))
.x(d => x(d.date))
.y1(d => y(d["seventh_layer_top"]))
.y0(d => y(d["sixth_layer_top"]));
lineGroup.append('g')
.selectAll('path')
.data(selectSeriesOne)
.join('path')
.attr('d', (d) => areaOne(d[1]))
.attr("class", function(d) {
return 'ts-line ts-line-' + d[0]; //
})
.attr("fill", function(d) {return dataDict2["first_value"].color})
.style("stroke", function(d) {return dataDict2["first_value"].lineColor})
.style("stroke-width", 1);
lineGroup.append('g')
.selectAll('path')
.data(selectSeriesTwo)
.join('path')
.attr('d', (d) => areaTwo(d[1]))
.attr("class", function(d) {
return 'ts-line ts-line-' + d[0]; //
})
.attr("fill", function(d) {return dataDict2["second_value"].color})
.style("stroke", function(d) {return dataDict2["second_value"].lineColor})
.style("stroke-width", 1);
lineGroup.append('g')
.selectAll('path')
.data(selectSeriesThree)
.join('path')
.attr('d', (d) => areaThree(d[1]))
.attr("class", function(d) {
return 'ts-line ts-line-' + d[0]; //
})
.attr("fill", function(d) {return dataDict2["third_value"].color})
.style("stroke", function(d) {return dataDict2["third_value"].lineColor})
.style("stroke-width", 1);
lineGroup.append('g')
.selectAll('path')
.data(selectSeriesFour)
.join('path')
.attr('d', (d) => areaFour(d[1]))
.attr("class", function(d) {
return 'ts-line ts-line-' + d[0]; //
})
.attr("fill", function(d) {return dataDict2["fourth_value"].color})
.style("stroke", function(d) {return dataDict2["fourth_value"].lineColor})
.style("stroke-width", 1);
lineGroup.append('g')
.selectAll('path')
.data(selectSeriesFive)
.join('path')
.attr('d', (d) => areaFive(d[1]))
.attr("class", function(d) {
return 'ts-line ts-line-' + d[0]; //
})
.attr("fill", function(d) {return dataDict2["fifth_value"].color})
.style("stroke", function(d) {return dataDict2["fifth_value"].lineColor})
.style("stroke-width", 1);
lineGroup.append('g')
.selectAll('path')
.data(selectSeriesSix)
.join('path')
.attr('d', (d) => areaSix(d[1]))
.attr("class", function(d) {
return 'ts-line ts-line-' + d[0]; //
})
.attr("fill", function(d) {return dataDict2["sixth_value"].color})
.style("stroke", function(d) {return dataDict2["sixth_value"].lineColor})
.style("stroke-width", 1);
lineGroup.append('g')
.selectAll('path')
.data(selectSeriesSeven)
.join('path')
.attr('d', (d) => areaSeven(d[1]))
.attr("class", function(d) {
return 'ts-line ts-line-' + d[0]; //
})
.attr("fill", function(d) {return dataDict2["seventh_value"].color})
.style("stroke", function(d) {return dataDict2["seventh_value"].lineColor})
.style("stroke-width", 1);
lineGroup.append('g')
.selectAll('path')
.data(selectSeriesEight)
.join('path')
.attr('d', (d) => areaEight(d[1]))
.attr("class", function(d) {
return 'ts-line ts-line-' + d[0]; //
})
.attr("fill", function(d) {return dataDict2["eighth_value"].color})
.style("stroke", function(d) {return dataDict2["eighth_value"].lineColor})
.style("stroke-width", 1);
// Tooltip Line
group.append("line")
.attr("class", "y-highlight")
.attr('x1', 0)
.attr('x2', 0)
.attr('y1', 0)
.attr('y2', innerHeight)
.style('stroke', '#d3d3d3')
.style('stroke-width', '3px')
.style('opacity', 0.85);
// Chart toolTip Area (defines where hover with be active)
let tipArea = group.append("g")
.attr("class", "tip-area");
tipArea.append('svg:rect') // append a rect to catch mouse movements on canvas
.attr('width', innerWidth)
.attr('height', innerHeight)
.attr('opacity', 0)
.attr('pointer-events', 'all')
.on('mouseover', (event) => {
console.log('mouseover');
})
.on('mousemove', (event) => {
console.log('mousemove');
// Get the date on the y axis for the mouse position
let invert = x.invert(d3.pointer(event)[0])
let bisect = d3.bisector(function(d) { return d; }).center;
let hoverDate = (selectDatesTwo[bisect(selectDatesAll, invert)]);
console.log(hoverDate);
// 1) Lookup/Get values by date 2) Filter by selection group 3) Sort by top values
let lookup = selectLookupAll.get(hoverDate.toISOString()).filter(d => selectGroupAll.includes(d.id)).sort((a, b) => d3.descending(a[selectTypeAll], b[selectTypeAll]));
// Display and position vertical line
d3.select("#" + chartid + " .y-highlight")
.attr('x1',x(hoverDate))
.attr('x2',x(hoverDate))
.style('opacity', 1);
// Custom toolTip Content
let tipContent = ``; // Start content string
// Interate over selected categories
lookup.map( d => {
tipContent += `<hr style='margin:2px; padding:0; border-top: 3px solid #333;'> <div class="chip-circle" style="background-color: ${dataDict2["eighth_value"].color};"></div> ${dataDict2["eighth_value"].label}: ${format.percent1d(d["Private Equity"])}<br><div class="chip-circle" style="background-color: ${dataDict2["seventh_value"].color};"></div> ${dataDict2["seventh_value"].label}: ${format.percent1d(d["Real Estate"])}<br><div class="chip-circle" style="background-color: ${dataDict2["sixth_value"].color};"></div> ${dataDict2["sixth_value"].label}: ${format.percent1d(d["Hedge Fund"])}<br><div class="chip-circle" style="background-color: ${dataDict2["fifth_value"].color};"></div> ${dataDict2["fifth_value"].label}: ${format.percent1d(d["Commodities"])}<br><div class="chip-circle" style="background-color: ${dataDict2["fourth_value"].color};"></div> ${dataDict2["fourth_value"].label}: ${format.percent1d(d["Other & Misc. Alternatives"])}<br><div class="chip-circle" style="background-color: ${dataDict2["third_value"].color};"></div> ${dataDict2["third_value"].label}: ${format.percent1d(d["Equities"])}<br><div class="chip-circle" style="background-color: ${dataDict2["second_value"].color};"></div> ${dataDict2["second_value"].label}: ${format.percent1d(d["Fixed Income"])}<br><div class="chip-circle" style="background-color: ${dataDict2["first_value"].color};"></div> ${dataDict2["first_value"].label}: ${format.percent1d(d["Cash"])}<br>`; // Add to content string
})
chartTip.style("left", () => {
if ((innerWidth - d3.pointer(event)[0]) < 199) {
return event.pageX - (150) + "px";
} else {
return event.pageX + 20 + "px";
}
})
.style("top", event.pageY + 5 + "px")
.style("display", "inline-block")
.html(`<div style="text-align: center; font-weight: 700;">${format.date1(hoverDate)}</div>${tipContent}`); // toolTip Content
})
.on('mouseout', (event) => {
console.log('mouseout');
chartTip.style("display", "none"); // Hide toolTip
d3.select("#" + chartid + " .y-highlight").style('opacity', 0); // Hide y-highlight line
d3.selectAll("#" + chartid + " .y-highlight-point").remove(); // Remove y-highlight points
});
// Annotations({
// element: svg,
// text: annotationOne,
// color: annotationOneColor,
// fontSize: annotationOneFont,
// fontWeight: annotationOneWeight,
// anchor: 'start',
// width: annotationOneWidth,
// x: annotationOneX,
// y: annotationOneY,
// lineHeight: 1.1
// })
// Annotations({
// element: svg,
// text: annotationTwo,
// color: annotationTwoColor,
// fontSize: annotationTwoFont,
// fontWeight: annotationTwoWeight,
// anchor: 'start',
// width: annotationTwoWidth,
// x: annotationTwoX,
// y: annotationTwoY,
// lineHeight: 1.1
// })
// Annotations({
// element: svg,
// text: annotationThree,
// color: annotationThreeColor,
// fontSize: annotationThreeFont,
// fontWeight: annotationThreeWeight,
// anchor: 'start',
// width: annotationThreeWidth,
// x: annotationThreeX,
// y: annotationThreeY,
// lineHeight: 1.1
// })
return svg.node();
}
arrTreasuryChart = {
let height = 375;
let margin = {top: 20, right: 20, bottom: 20, left: 50}
let innerHeight = height - margin.top - margin.bottom;
let innerWidth = width - margin.left - margin.right;
let chartid = "arr-treasury-chart";
let selectType = "Treasury";
const selectGroup = "United States";
let filtered = aTData.filter(d => selectGroup.match(d.State));
let selectLookup = d3.group(filtered, d => d.date.toISOString());
let selectDates = Array.from(selectLookup).map(d => d[1][0].date);
let extData = d3.extent(filtered, d => d["ARR"]);
let extDates = d3.extent(filtered, d => d.date);
let selectSeries = d3.groups(filtered, d => d.State);
// Highlight
const selectGroupHighlight = "United States";
let filteredHighlight = aTData.filter(d => selectGroupHighlight == d.State);
let selectSeriesHighlight = d3.groups(filteredHighlight, d => d.State);
const svg = d3.create("svg")
.attr("viewBox", [0, 0, width, height])
.attr("id", chartid);
const group = svg.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
let x = d3.scaleTime()
.range([0, width - margin.left - margin.right])
.domain(extDates);
let y = d3.scaleLinear()
.range([innerHeight, 0])
.domain([0, 0.1])
.nice();
let xAxis = d3.axisBottom(x);
let yAxis = d3.axisLeft(y);
// X-AXIS
if (width > 700) {
group.append("g")
.attr("class", "x axis")
.attr("transform", "translate("+ 0 + "," + innerHeight + ")")
.call(xAxis)
.call(d3.axisBottom(x)
.tickFormat(d3.timeFormat("%Y"))
.tickSize(5)
.ticks(10)
)
.call(g => g.selectAll(".domain").remove())
.style('color', 'rgb(129, 129, 129)')
.style("font-size", "12px")
.style('font-family', "'Open Sans', sans-serif")
.attr("stroke-opacity", 0.5);
} else {
group.append("g")
.attr("class", "x axis")
.attr("transform", "translate("+ 0 + "," + innerHeight + ")")
.call(xAxis)
.call(d3.axisBottom(x)
.tickFormat(d3.timeFormat("'%y"))
.tickSize(10)
)
.call(g => g.selectAll(".domain").remove())
.style('color', 'rgb(129, 129, 129)')
.style('font-family', "'Open Sans', sans-serif")
.attr("stroke-opacity", 0.5);
}
// Y-Axis
group.append("g")
.attr("class", "y axis")
.call(yAxis)
.call(d3.axisLeft(y)
.ticks(6)
.tickFormat((d) => d3.format(`.0%`)(d)))
.call(g => g.select(".domain").remove())
.call(g => g.selectAll(".tick line").clone()
.attr("x2", innerWidth + 50))
.call(g => g.selectAll(".tick text")
.style("font-size", "12px")
.style("font-weight", 400)
.attr("fill", "rgb(129, 129, 129)")
.attr("transform", `translate(0, -7)`))
.call(g => g.selectAll(".tick line")
.attr("transform", `translate(-40, 0)`)
.attr("stroke-opacity", 0.2))
.call(g => g.append("text")
.attr("text-anchor", "middle")
.style("font-size", "16px")
.attr("fill", "currentColor"))
.attr('font-family', "'Open Sans', sans-serif");
// Lines
let lineGroup = group.append("g")
.attr("class", "line-group");
let line = d3.line()
.defined(d => !isNaN(d[selectType]))
.curve(d3.curveCardinal)
.x(d => x(d.date))
.y(d => y(d[selectType]));
lineGroup.append('g')
.selectAll('path')
.data(selectSeries)
.join('path')
.attr('d', (d) => line(d[1]))
.attr("class", function(d) {
return 'ts-line ts-line-' + d[0]; //
})
.attr("fill", "none")
.style("stroke", function(d) {return dataDict3["Treasury"].lineColor})
// .style("opacity", 0.5)
.style("stroke-width", 4);
// Lines 2
let lineGroup2 = group.append("g")
.attr("class", "line-group");
let line2 = d3.line()
.defined(d => !isNaN(d["ARR"]))
.curve(d3.curveCardinal)
.x(d => x(d.date))
.y(d => y(d["ARR"]));
lineGroup2.append('g')
.selectAll('path')
.data(selectSeries)
.join('path')
.attr('d', (d) => line2(d[1]))
.attr("class", function(d) {
return 'ts-line ts-line-' + d[0]; //
})
.attr("fill", "none")
.style("stroke", function(d) {return dataDict3["ARR"].lineColor})
// .style("opacity", 0.5)
.style("stroke-width", 4);
// Tooltip Line
group.append("line")
.attr("class", "y-highlight")
.attr('x1', 0)
.attr('x2', 0)
.attr('y1', 0)
.attr('y2', innerHeight)
.style('stroke', '#d3d3d3')
.style('stroke-width', '3px')
.style('opacity', 0.85);
// Chart toolTip Area (defines where hover with be active)
let tipArea = group.append("g")
.attr("class", "tip-area");
tipArea.append('svg:rect') // append a rect to catch mouse movements on canvas
.attr('width', innerWidth)
.attr('height', innerHeight)
.attr('opacity', 0)
.attr('pointer-events', 'all')
.on('mouseover', (event) => {
console.log('mouseover');
})
.on('mousemove', (event) => {
console.log('mousemove');
// Get the date on the y axis for the mouse position
let invert = x.invert(d3.pointer(event)[0])
let bisect = d3.bisector(function(d) { return d; }).center;
let hoverDate = (selectDates[bisect(selectDates, invert)]);
console.log(hoverDate);
// 1) Lookup/Get values by date 2) Filter by selection group 3) Sort by top values
let lookup = selectLookup.get(hoverDate.toISOString()).filter(d => selectGroup.includes(d.State)).sort((a, b) => d3.descending(a[selectType], b[selectType]));
// Display and position vertical line
d3.select("#" + chartid + " .y-highlight")
.attr('x1',x(hoverDate))
.attr('x2',x(hoverDate))
.style('opacity', 1);
// Custom toolTip Content
let tipContent = `<hr style='margin:2px; padding:0; border-top: 3px solid #333;'>`; // Start content string
// Interate over selected categories
lookup.map( d => {
tipContent += `
<div class="chip-circle" style="background-color: #f63;"></div> ARR: ${d3.format(".1%")(d["ARR"])} <br>
<div class="chip-circle" style="background-color: #2879cb;"></div> Treasury: ${d3.format(".1%")(d["Treasury"])} `
})
chartTip.style("left", () => {
if ((innerWidth - d3.pointer(event)[0]) < 199) {
return event.pageX - (200) + "px";
} else {
return event.pageX + 20 + "px";
}
})
.style("top", event.pageY + 5 + "px")
.style("display", "inline-block")
.html(`<strong>${format.date2(hoverDate)}</strong>${tipContent}`); // toolTip Content
})
.on('mouseout', (event) => {
console.log('mouseout');
chartTip.style("display", "none"); // Hide toolTip
d3.select("#" + chartid + " .y-highlight").style('opacity', 0); // Hide y-highlight line
d3.selectAll("#" + chartid + " .y-highlight-point").remove(); // Remove y-highlight points
});
// Annotations({
// element: svg,
// text: annotationOne,
// color: annotationOneColor,
// fontSize: annotationOneFont,
// fontWeight: annotationOneWeight,
// anchor: 'start',
// width: annotationOneWidth,
// x: annotationOneX,
// y: annotationOneY,
// lineHeight: 1.1
// })
// Annotations({
// element: svg,
// text: annotationTwo,
// color: annotationTwoColor,
// fontSize: annotationTwoFont,
// fontWeight: annotationTwoWeight,
// anchor: 'start',
// width: annotationTwoWidth,
// x: annotationTwoX,
// y: annotationTwoY,
// lineHeight: 1.1
// })
// Annotations({
// element: svg,
// text: annotationThree,
// color: annotationThreeColor,
// fontSize: annotationThreeFont,
// fontWeight: annotationThreeWeight,
// anchor: 'start',
// width: annotationThreeWidth,
// x: annotationThreeX,
// y: annotationThreeY,
// lineHeight: 1.1
// })
return svg.node();
}
function formValue(form) {
const object = {};
for (const input of form.elements) {
if (input.disabled || !input.hasAttribute("name")) continue;
let value = input.value;
switch (input.type) {
case "range":
case "number": {
value = input.valueAsNumber;
break;
}
case "date": {
value = input.valueAsDate;
break;
}
case "radio": {
if (!input.checked) continue;
break;
}
case "checkbox": {
if (input.checked) value = true;
else if (input.name in object) continue;
else value = false;
break;
}
case "file": {
value = input.multiple ? input.files : input.files[0];
break;
}
case "select-multiple": {
value = Array.from(input.selectedOptions, option => option.value);
break;
}
}
object[input.name] = value;
}
return object;
}
function form(form) {
const container = html`<div>${form}`;
form.addEventListener("submit", event => event.preventDefault());
form.addEventListener("change", () => container.dispatchEvent(new CustomEvent("input")));
form.addEventListener("input", () => container.value = formValue(form));
container.value = formValue(form);
return container
}
html`
<style>
.data-table table {
width: 100%;
border-collapse: separate;
border-spacing: 0.1rem;
overflow-y: scroll;
font-size: 16px;
}
.data-table td,
.data-table th {
padding: 0.3rem;
word-wrap: break-word;
border-bottom: 0;
text-align: center;
font-size: 16px;
}
.data-table td:nth-child(1) {
text-align: left;
}
.data-table th {
color: #fff;
background-color: #384354;
position: sticky;
}
.data-table td {
background-color: #fff;
width: 200px;
}
</style>
`
style_sheet = html`<style>
@import url('https://fonts.googleapis.com/css2?family=Open+Sans:wght@300;400;500;600;700;800&display=swap');
body {
font-family: 'Open Sans', sans-serif;
}
select {
font-family: 'Open Sans', sans-serif;
// border-radius: 20px;
background-color: #fff;
}
.chip-circle {
border-radius: 50%;
display: inline-block;
position: relative;
width: 10px;
height: 10px;
}
label {
margin-top: 20px;
padding: 10px;
padding-right:15px;
text-align: center;
cursor: pointer;
background-color: #fff;
color: #2e3745;
font-weight: 600;
}
input[type="range"] {
-webkit-appearance: none;
border: none;
margin: 18px 0;
width: 100%;
box-shadow: -2px -2px 8px white, 2px 2px 8px rgba(black, 0.5);
}
input[type="range"]:focus {
outline: none;
}
input[type="range"]::-webkit-slider-runnable-track {
border: none;
width: 100%;
height: 5px;
cursor: pointer;
background: #fff;
border-radius: 10px;
box-shadow: rgb(204, 219, 232) 3px 3px 6px 0px inset, rgba(255, 255, 255, 0.5) -3px -3px 6px 1px inset;
}
input[type="range"]::-webkit-slider-thumb {
height: 15px;
width: 15px;
border-radius: 30px;
box-shadow: 0px 0px 4px 1px rgba(0,0,0,0.37);
/* border: 1px solid #333; */
background: #fff;
cursor: pointer;
-webkit-appearance: none;
margin-top: -5px;
}
input[type="range"]:focus::-webkit-slider-runnable-track {
background: #fff;
border: none;
}
input[type="range"]::-moz-range-track {
border: none;
width: 100%;
height: 5px;
cursor: pointer;
background: #fff;
border-radius: 10px;
box-shadow: rgb(204, 219, 232) 3px 3px 6px 0px inset, rgba(255, 255, 255, 0.5) -3px -3px 6px 1px inset;
}
input[type="range"]::-moz-range-thumb {
height: 15px;
width: 15px;
border-radius: 30px;
box-shadow: 0px 0px 4px 1px rgba(0,0,0,0.37);
/* border: 1px solid #333; */
background: #fff;
cursor: pointer;
-webkit-appearance: none;
margin-top: -5px;
}
input[type="range"]::-ms-track {
border: none;
width: 100%;
height: 5px;
cursor: pointer;
background: transparent;
border-color: transparent;
border-width: 16px 0;
color: transparent;
}
input[type="range"]::-ms-fill-lower {
background: #e0e0e0;
border-radius: 2.6px;
}
input[type="range"]::-ms-fill-upper {
background: #e0e0e0;
border-radius: 2.6px;
}
input[type="range"]::-ms-thumb {
border: none;
height: 20px;
width: 16px;
border-radius: 3px;
background: #ffffff;
cursor: pointer;
}
input[type="range"]:focus::-ms-fill-lower {
background: #fff;
}
input[type="range"]:focus::-ms-fill-upper {
background: #fff;
}
#grid-3 {
display: grid;
grid-template-columns: auto auto auto;
background-color: #fff;
}
#grid-4 {
display: grid;
grid-template-columns: auto auto auto auto;
background-color: #fff;
}
#grid-4-full {
display: grid;
grid-template-columns: auto auto auto auto;
background-color: #fff;
border-top: 1px solid #d3d3d3;
border-right: 1px solid #d3d3d3;
border-bottom: 1px solid #d3d3d3;
border-left: 1px solid #d3d3d3;
}
#grid-4-top {
display: grid;
grid-template-columns: auto auto auto auto;
background-color: #fff;
border-top: 1px solid #d3d3d3;
border-right: 1px solid #d3d3d3;
border-left: 1px solid #d3d3d3;
}
#grid-4-bottom {
display: grid;
grid-template-columns: auto auto auto auto;
background-color: #fff;
border-bottom: 1px solid #d3d3d3;
border-right: 1px solid #d3d3d3;
border-left: 1px solid #d3d3d3;
}
.grid-item {
background-color: rgba(255, 255, 255, 0.8);
border: 1px solid #d3d3d3;
padding: 20px;
font-size: 16px;
text-align: left;
}
</style>`
tooltipStyle = html`
<style>
.toolTip {
position: absolute;
display: none;
min-width: 30px;
border-radius: 0;
height: auto;
background: #fff;
border: 1px solid #d3d3d3;
padding: 4px 8px;
font-size: .85rem;
text-align: left;
z-index: 1000;
}
.eventTip { max-width: 140px;}
.chartTip { max-width: 240px;}
.tip-values {
font-weight: bold;
color: #787878;
}
.tip-values .secondary{
font-weight: normal;
}
.tip-table {
font-size: 12px;
text-align: right;
color: #787878;
}
.tip-table td, .tip-table th {
padding-right: 4px;
padding-left: 4px;
}
.tip-table th {
font-size: 10px;
text-align: right;
color: #AAA;
vertical-align: bottom;
}
.tip-table .row-header {
text-align: left;
}
.tip-table .primary {
font-weight: bold;
}
</style>
`
html`<style>
.info-title {
margin-bottom: 32px;
text-align: center;
}
.info-title h1 {
max-width: 10000px;
}
.info-box {
padding: 24px;
}
.info-wrapper {
display: flex;
justify-content: center;
}
.wrapper-2 {
display: grid;
grid-template-columns: 1fr 1fr;
}
.wrapper-3 {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
}
.box-title {
padding: 20px;
text-align: center;
font-weight: 500;
}
.wrapper-4 {
display: grid;
grid-template-columns: 1fr 1fr 1fr 1fr;
}
</style>
`
viewof info_box1 = html`
<div style="font-size: ${fontParam2}; font-family: 'Open Sans', sans-serif;">
${stateInfo[currentState2].info}
</div>
`
viewof currentState2 = {
const mapWidth = widthParam,
margin = marginParam;
const gridMap = d3.select(
Plot.plot({
width: width,
height: mapHeightParam,
padding: 0,
marginTop: 10,
marginBottom: 10,
marginLeft: margin,
marginRight: margin,
x: { axis: null },
y: { axis: null },
color: { type: "identity" },
marks: cells
})
);
let timeout,
focus = null;
const rect = gridMap.selectAll("rect");
gridMap
.on("pointerout", restore)
.on("click", handleClick)
.style("cursor", "default");
const map = html`<div>${gridMap.node()}`;
updateState(null);
return map;
function highlight(e) {
if (focus !== null) return;
const index = getTargetIndex(e);
fill(index, true);
if (timeout) clearTimeout(timeout);
timeout = setTimeout(
() => updateState(index >= 0 ? lookupState(index) : null),
1
);
}
function restore(e) {
if (focus !== null) return;
const index = getTargetIndex(e);
fill(index);
}
function fill(index, highlight, darker) {
if (index >= 0) {
const color = highlight ? "#f63" : colors(jsonData[index].total);
d3.select(rect.nodes()[index])
.attr("fill", darker ? d3.color(color).darker(darker) : color)
.attr("stroke-width", highlight ? 1 : 0);
}
}
function handleClick(e) {
const index = getTargetIndex(e);
if (focus === index) {
focus = null;
return;
}
if (focus !== null) fill(focus);
if (index >= 0) {
fill(index, true);
focus = index;
updateState(lookupState(index));
} else {
focus = null;
updateState(null);
}
}
function handlePointerDown(e) {
const index = getTargetIndex(e);
if (index >= 0) fill(index, true, 0.25);
}
function handlePointerUp(e) {
const index = getTargetIndex(e);
if (index >= 0) fill(index, true);
}
function getTargetIndex(e) {
let tar = e.target;
if (tar instanceof SVGTSpanElement) tar = tar.parentElement;
return tar instanceof SVGTextElement || tar instanceof SVGRectElement
? [...tar.parentElement.children].indexOf(tar)
: -1;
}
function lookupState(index) {
return jsonData[index].code;
}
function updateState(state) {
map.value = state;
map.dispatchEvent(new CustomEvent("input"));
}
}
stateInfo = ({
"null": {
status: "impacted",
info: `
<h2 style="margin-bottom: 5px;">United States</h2>
<b>Public entities that have signed on to <span style="text-decoration: underline">Ceres Investor Network</span>:</b>
<ul style="margin-top: 0;">
<li>11 public pension plans in 6 states</li>
<li>8 state treasurer offices</li>
<li>2 state controller offices plus New York City's office</li>
<li>3 state investment boards</li>
</ul>
<b>Public entities that have signed on to <span style="text-decoration: underline">Climate Action 100+</span>:</b>
<ul style="margin-top: 0;">
<li>16 public pension plans in 6 states</li>
<li>3 state treasurer offices</li>
<li>4 state investment boards</li>
</ul>
`
},
"AL": {
status: "impacted",
info: `
<h2>Alabama</h2>
<p>No government entity in Alabama signed on to Ceres Investor Network or Climate Action 100+.</p>
`
},
"AK": {
status: "impacted",
info: `
<h2>Alaska</h2>
<p>No government entity in Alaska signed on to Ceres Investor Network or Climate Action 100+.</p>
`
},
"AR": {
status: "impacted",
info: `
<h2>Arkansas</h2>
<p>No government entity in Arkansas signed on to Ceres Investor Network or Climate Action 100+.</p>
`
},
"AZ": {
status: "impacted",
info: `
<h2>Arizona</h2>
<p>No government entity in Arizona signed on to Ceres Investor Network or Climate Action 100+.</p>
`
},
"CA": {
status: "impacted",
info: `
<h2>California</h2>
<b>Public entities that have signed on to <span style="text-decoration: underline">Ceres Investor Network</span>:</b>
<ul style="margin-top: 0;">
<li>California Public Employees' Retirement System</li>
<li>California State Teachers' Retirement System</li>
<li>California State Controller's Office</li>
<li>California State Treasurer's Office</li>
<li>San Francisco Employees' Retirement System</li>
<li>East Bay Municipal Utility District Employees Retirement System</li>
</ul>
<b>Public entities that have signed on to <span style="text-decoration: underline">Climate Action 100+</span>:</b>
<ul style="margin-top: 0;">
<li>California Public Employees' Retirement System</li>
<li>California State Teachers' Retirement System</li>
<li>Los Angeles County Employees Retirement Association</li>
<li>East Bay Municipal Utility District Employees Retirement System</li>
</ul>
`
},
"CO": {
status: "impacted",
info: `
<h2>Colorado</h2>
<p>No government entity in Colorado signed on to Ceres Investor Network or Climate Action 100+.</p>
`
},
"CT": {
status: "impacted",
info: `
<h2>Connecticut</h2>
<b>Public entities that have signed on to <span style="text-decoration: underline">Ceres Investor Network</span>:</b>
<ul style="margin-top: 0;">
<li>Connecticut Office of the State Treasurer</li>
</ul>
<b>Public entities that have signed on to <span style="text-decoration: underline">Climate Action 100+</span>:</b>
<ul style="margin-top: 0;">
<li>Connecticut Retirement Plans & Trusts</li>
</ul>
`
},
"DC": {
status: "impacted",
info: `
<h2>District of Columbia</h2>
<p>No government entity in District of Columbia signed on to Ceres Investor Network or Climate Action 100+.</p>
`
},
"DE": {
status: "impacted",
info: `
<h2>Delaware</h2>
<p>No government entity in Delaware signed on to Ceres Investor Network or Climate Action 100+.</p>
`
},
"FL": {
status: "impacted",
info: `
<h2>Florida</h2>
<p>No government entity in Florida signed on to Ceres Investor Network or Climate Action 100+.</p>
`
},
"GA": {
status: "impacted",
info: `
<h2>Georgia</h2>
<p>No government entity in Georgia signed on to Ceres Investor Network or Climate Action 100+.</p>
`
},
"HI": {
status: "impacted",
info: `
<h2>Hawaii</h2>
<ul>
<b style="margin-left: -20px;">Climate Action 100+</b>
<li>Employees’ Retirement System of the State of Hawaii</li>
</ul>
`
},
"KY": {
status: "impacted",
info: `
<h2>Kentucky</h2>
<p>No government entity in Kentucky signed on to Ceres Investor Network or Climate Action 100+.</p>
`
},
"IA": {
status: "impacted",
info: `
<h2>Iowa</h2>
<p>No government entity in Iowa signed on to Ceres Investor Network or Climate Action 100+.</p>
`
},
"ID": {
status: "impacted",
info: `
<h2>Idaho</h2>
<p>No government entity in Idaho signed on to Ceres Investor Network or Climate Action 100+.</p>
`
},
"IN": {
status: "impacted",
info: `
<h2>Indiana</h2>
<p>No government entity in Indiana signed on to Ceres Investor Network or Climate Action 100+.</p>
`
},
"IL": {
status: "impacted",
info: `
<h2>Illinois</h2>
<b>Public entities that have signed on to <span style="text-decoration: underline">Ceres Investor Network</span>:</b>
<ul style="margin-top: 0;">
<li>Illinois State Treasurer</li>
</ul>
<b>Public entities that have signed on to <span style="text-decoration: underline">Climate Action 100+</span>:</b>
<ul style="margin-top: 0;">
<li>Illinois State Treasurer</li>
<li>City of Chicago</li>
</ul>
`
},
"KS": {
status: "impacted",
info: `
<h2>Kansas</h2>
<p>No government entity in Kansas signed on to Ceres Investor Network or Climate Action 100+.</p>
`
},
"LA": {
status: "impacted",
info: `
<h2>Louisiana</h2>
<p>No government entity in Louisiana signed on to Ceres Investor Network or Climate Action 100+.</p>
`
},
"MA": {
status: "impacted",
info: `
<h2>Massachusetts</h2>
<b>Public entities that have signed on to <span style="text-decoration: underline">Ceres Investor Network</span>:</b>
<ul style="margin-top: 0;">
<li>Massachusetts Office of the State Treasurer</li>
<li>City of Boston</li>
<li>Boston Retirement System</li>
</ul>
`
},
"MI": {
status: "impacted",
info: `
<h2>Michigan</h2>
<p>No government entity in Michigan signed on to Ceres Investor Network or Climate Action 100+.</p>
`
},
"MD": {
status: "impacted",
info: `
<h2>Maryland</h2>
<b>Public entities that have signed on to <span style="text-decoration: underline">Ceres Investor Network</span>:</b>
<ul style="margin-top: 0;">
<li>Maryland State Retirement and Pension System</li>
<li>Montgomery County Employees' Retirement System</li>
</ul>
<b>Public entities that have signed on to <span style="text-decoration: underline">Climate Action 100+</span>:</b>
<ul style="margin-top: 0;">
<li>Maryland State Retirement and Pension System</li>
</ul>
`
},
"ME": {
status: "impacted",
info: `
<h2>Maine</h2>
<b>Public entities that have signed on to <span style="text-decoration: underline">Ceres Investor Network</span>:</b>
<ul style="margin-top: 0;">
<li>Maine Public Employee Retirement System</li>
</ul>
`
},
"MN": {
status: "impacted",
info: `
<h2>Minnesota</h2>
<b>Public entities that have signed on to <span style="text-decoration: underline">Ceres Investor Network</span>:</b>
<ul style="margin-top: 0;">
<li>Minnesota State Board of Investment</li>
</ul>
<b>Public entities that have signed on to <span style="text-decoration: underline">Climate Action 100+</span>:</b>
<ul style="margin-top: 0;">
<li>Minnesota State Board of Investment</li>
</ul>
`
},
"MO": {
status: "impacted",
info: `
<h2>Missouri</h2>
<p>No government entity in Missouri signed on to Ceres Investor Network or Climate Action 100+.</p>
`
},
"MS": {
status: "impacted",
info: `
<h2>Mississippi</h2>
<p>No government entity in Mississippi signed on to Ceres Investor Network or Climate Action 100+.</p>
`
},
"MT": {
status: "impacted",
info: `
<h2>Missouri</h2>
<p>No government entity in Missouri signed on to Ceres Investor Network or Climate Action 100+.</p>
`
},
"NC": {
status: "impacted",
info: `
<h2>Montana</h2>
<p>No government entity in Montana signed on to Ceres Investor Network or Climate Action 100+.</p>
`
},
"ND": {
status: "impacted",
info: `
<h2>North Dakota</h2>
<p>No government entity in North Dakota signed on to Ceres Investor Network or Climate Action 100+.</p>
`
},
"NE": {
status: "impacted",
info: `
<h2>Nebraska</h2>
<p>No government entity in Nebraska signed on to Ceres Investor Network or Climate Action 100+.</p>
`
},
"NH": {
status: "impacted",
info: `
<h2>New Hampshire</h2>
<p>No government entity in New Hampshire signed on to Ceres Investor Network or Climate Action 100+.</p>
`
},
"NJ": {
status: "impacted",
info: `
<h2>New Jersey</h2>
<b>Public entities that have signed on to <span style="text-decoration: underline">Ceres Investor Network</span>:</b>
<ul style="margin-top: 0;">
<li>New Jersey Division of Investment</li>
<br>
</ul>
<b>Public entities that have signed on to <span style="text-decoration: underline">Climate Action 100+</span>:</b>
<ul style="margin-top: 0;">
<li>New Jersey Division of Investment</li>
</ul>
`
},
"NM": {
status: "impacted",
info: `
<h2>New Mexico</h2>
<b>Public entities that have signed on to <span style="text-decoration: underline">Ceres Investor Network</span>:</b>
<ul style="margin-top: 0;">
<li>New Mexico State Treasurer's Office</li>
</ul>
`
},
"NY": {
status: "impacted",
info: `
<h2>New York</h2>
<b>Public entities that have signed on to <span style="text-decoration: underline">Ceres Investor Network</span>:</b>
<ul style="margin-top: 0;">
<li>New York State Teachers' Retirement System</li>
<li>New York State Comptroller</li>
<li>New York City Employees' Retirement System</li>
<li>New York City Office of the Comptroller</li>
</ul>
<b>Public entities that have signed on to <span style="text-decoration: underline">Climate Action 100+</span>:</b>
<ul style="margin-top: 0;">
<li>New York State Common Retirement Fund</li>
<li>New York State Teachers' Retirement System</li>
<li>New York City Pension Funds</li>
</ul>
`
},
"NV": {
status: "impacted",
info: `
<h2>Nevada</h2>
<p>No government entity in Nevada signed on to Ceres Investor Network or Climate Action 100+.</p>
`
},
"OH": {
status: "impacted",
info: `
<h2>Ohio</h2>
<p>No government entity in Ohio signed on to Ceres Investor Network or Climate Action 100+.</p>
`
},
"OK": {
status: "impacted",
info: `
<h2>Oklahoma</h2>
<p>No government entity in Oklahoma signed on to Ceres Investor Network or Climate Action 100+.</p>
`
},
"OR": {
status: "impacted",
info: `
<h2>Oregon</h2>
<b>Public entities that have signed on to <span style="text-decoration: underline">Ceres Investor Network</span>:</b>
<ul style="margin-top: 0;">
<li>Oregon Office of the State Treasurer</li>
<br>
</ul>
<b>Public entities that have signed on to <span style="text-decoration: underline">Climate Action 100+</span>:</b>
<ul style="margin-top: 0;">
<li>Oregon Office of the State Treasurer</li>
</ul>
`
},
"PA": {
status: "impacted",
info: `
<h2>Pennsylvania</h2>
<p>No government entity in Pennsylvania signed on to Ceres Investor Network or Climate Action 100+.</p>
`
},
"RI": {
status: "impacted",
info: `
<h2>Rhode Island</h2>
<b>Public entities that have signed on to <span style="text-decoration: underline">Ceres Investor Network</span>:</b>
<ul style="margin-top: 0;">
<li>Rhode Island Office of the General Treasurer</li>
</ul>
`
},
"SC": {
status: "impacted",
info: `
<h2>South Carolina</h2>
<p>No government entity in South Carolina signed on to Ceres Investor Network or Climate Action 100+.</p>
`
},
"SD": {
status: "impacted",
info: `
<h2>South Dakota</h2>
<p>No government entity in South Dakota signed on to Ceres Investor Network or Climate Action 100+.</p>
`
},
"TN": {
status: "impacted",
info: `
<h2>Tennessee</h2>
<p>No government entity in Tennessee signed on to Ceres Investor Network or Climate Action 100+.</p>
`
},
"TX": {
status: "impacted",
info: `
<h2>Texas</h2>
<p>No government entity in Texas signed on to Ceres Investor Network or Climate Action 100+.</p>
`
},
"UT": {
status: "impacted",
info: `
<h2>Utah</h2>
<p>No government entity in Utah signed on to Ceres Investor Network or Climate Action 100+.</p>
`
},
"VA": {
status: "impacted",
info: `
<h2>Virginia</h2>
<p>No government entity in Virginia signed on to Ceres Investor Network or Climate Action 100+.</p>
`
},
"VT": {
status: "impacted",
info: `
<h2>Vermont</h2>
<b>Public entities that have signed on to <span style="text-decoration: underline">Ceres Investor Network</span>:</b>
<ul style="margin-top: 0;">
<li>Vermont Office of the State Treasurer</li>
</ul>
<b>Public entities that have signed on to <span style="text-decoration: underline">Climate Action 100+</span>:</b>
<ul style="margin-top: 0;">
<li>Vermont Pension Investment Committee</li>
<li>Vermont Office of the State Treasurer</li>
</ul>
`
},
"WA": {
status: "impacted",
info: `
<h2>Washington</h2>
<b>Public entities that have signed on to <span style="text-decoration: underline">Ceres Investor Network</span>:</b>
<ul style="margin-top: 0;">
<li>Washington State Investment Board</li>
<li>Seattle City Employees' Retirement System</li>
</ul>
<b>Public entities that have signed on to <span style="text-decoration: underline">Climate Action 100+</span>:</b>
<ul style="margin-top: 0;">
<li>Washington State Investment Board</li>
<li>Seattle City Employees' Retirement System</li>
</ul>
`
},
"WI": {
status: "impacted",
info: `
<h2>Wisconsin</h2>
<p>No government entity in Wisconsin signed on to Ceres Investor Network or Climate Action 100+.</p>
`
},
"WV": {
status: "impacted",
info: `
<h2>West Virginia</h2>
<p>No government entity in West Virginia signed on to Ceres Investor Network or Climate Action 100+.</p>
`
},
"WY": {
status: "impacted",
info: `
<h2>Wyoming</h2>
<p>No government entity in Wyoming signed on to Ceres Investor Network or Climate Action 100+.</p>
`
}
});
cells = {
const threshold = extent[1] / 3;
return [
Plot.cell(jsonData, {
x: "col",
y: "row",
rx: 0,
ry: 6,
fill: (d) => colors(d.total),
stroke: '#fff',
strokeWidth: 0,
inset: 2
}),
Plot.text(jsonData, {
x: "col",
y: "row",
fontWeight: "bold",
fontSize: fontParam,
fill: "#333",
fill: (d) => (d.total < threshold ? "#333" : "#eee"),
// Plot 0.4 tspan
text: (d) => `${d.code}`
})
];
};
colors = d3.scaleSequential(d3.interpolateHsl('#d3d3d3', '#228B22')).domain(extent);
extent = d3.extent(jsonData.map(d => d.total));
mapHeightParam = {if (mobileTest == false) {
return 500
} else {
return 275
}};
widthParam = width;
fontParam = {
if (mobileTest == false) {
return "25px"
} else {
return "11px"
}
};
fontParam2 = "15px";
marginParam = (width - widthParam) / 2.5;
jsonData = [
{
"col": 11,
"row": 0,
"code": "ME",
"state": "Maine",
"total": 1
},
{
"col": 0,
"row": 1,
"code": "AK",
"state": "Alaska",
"total": 0
},
{
"col": 6,
"row": 1,
"code": "WI",
"state": "Wisconsin",
"total": 0
},
{
"col": 10,
"row": 1,
"code": "VT",
"state": "Vermont",
"total": 1
},
{
"col": 11,
"row": 1,
"code": "NH",
"state": "New Hampshire",
"total": 0
},
{
"col": 1,
"row": 2,
"code": "WA",
"state": "Washington",
"total": 1
},
{
"col": 2,
"row": 2,
"code": "ID",
"state": "Idaho",
"total": 0
},
{
"col": 3,
"row": 2,
"code": "MT",
"state": "Montana",
"total": 0
},
{
"col": 4,
"row": 2,
"code": "ND",
"state": "North Dakota",
"total": 0
},
{
"col": 5,
"row": 2,
"code": "MN",
"state": "Minnesota",
"total": 1
},
{
"col": 6,
"row": 2,
"code": "IL",
"state": "Illinois",
"total": 1
},
{
"col": 7,
"row": 2,
"code": "MI",
"state": "Michigan",
"total": 0
},
{
"col": 9,
"row": 2,
"code": "NY",
"state": "New York",
"total": 1
},
{
"col": 10,
"row": 2,
"code": "MA",
"state": "Massachusetts",
"total": 1
},
{
"col": 1,
"row": 3,
"code": "OR",
"state": "Oregon",
"total": 1
},
{
"col": 2,
"row": 3,
"code": "NV",
"state": "Nevada",
"total": 0
},
{
"col": 3,
"row": 3,
"code": "WY",
"state": "Wyoming",
"total": 0
},
{
"col": 4,
"row": 3,
"code": "SD",
"state": "South Dakota",
"total": 0
},
{
"col": 5,
"row": 3,
"code": "IA",
"state": "Iowa",
"total": 0
},
{
"col": 6,
"row": 3,
"code": "IN",
"state": "Indiana",
"total": 0
},
{
"col": 7,
"row": 3,
"code": "OH",
"state": "Ohio",
"total": 0
},
{
"col": 8,
"row": 3,
"code": "PA",
"state": "Pennsylvania",
"total": 0
},
{
"col": 9,
"row": 3,
"code": "NJ",
"state": "New Jersey",
"total": 1
},
{
"col": 10,
"row": 3,
"code": "CT",
"state": "Connecticut",
"total": 1
},
{
"col": 11,
"row": 3,
"code": "RI",
"state": "Rhode Island",
"total": 1
},
{
"col": 1,
"row": 4,
"code": "CA",
"state": "California",
"total": 1
},
{
"col": 2,
"row": 4,
"code": "UT",
"state": "Utah",
"total": 0
},
{
"col": 3,
"row": 4,
"code": "CO",
"state": "Colorado",
"total": 0
},
{
"col": 4,
"row": 4,
"code": "NE",
"state": "Nebraska",
"total": 0
},
{
"col": 5,
"row": 4,
"code": "MO",
"state": "Missouri",
"total": 0
},
{
"col": 6,
"row": 4,
"code": "KY",
"state": "Kentucky",
"total": 0
},
{
"col": 7,
"row": 4,
"code": "WV",
"state": "West Virginia",
"total": 0
},
{
"col": 8,
"row": 4,
"code": "VA",
"state": "Virginia",
"total": 0
},
{
"col": 9,
"row": 4,
"code": "MD",
"state": "Maryland",
"total": 1
},
{
"col": 10,
"row": 4,
"code": "DE",
"state": "Delaware",
"total": 0
},
{
"col": 2,
"row": 5,
"code": "AZ",
"state": "Arizona",
"total": 0
},
{
"col": 3,
"row": 5,
"code": "NM",
"state": "New Mexico",
"total": 1
},
{
"col": 4,
"row": 5,
"code": "KS",
"state": "Kansas",
"values": [
18493
],
"total": 0
},
{
"col": 5,
"row": 5,
"code": "AR",
"state": "Arkansas",
"total": 0
},
{
"col": 6,
"row": 5,
"code": "TN",
"state": "Tennessee",
"total": 0
},
{
"col": 7,
"row": 5,
"code": "NC",
"state": "North Carolina",
"total": 0
},
{
"col": 8,
"row": 5,
"code": "SC",
"state": "South Carolina",
"total": 0
},
{
"col": 9,
"row": 5,
"code": "DC",
"state": "District of Columbia",
"total": 0
},
{
"col": 0,
"row": 6,
"code": "HI",
"state": "Hawaii",
"total": 1
},
{
"col": 4,
"row": 6,
"code": "OK",
"state": "Oklahoma",
"total": 0
},
{
"col": 5,
"row": 6,
"code": "LA",
"state": "Louisiana",
"total": 0
},
{
"col": 6,
"row": 6,
"code": "MS",
"state": "Mississippi",
"total": 0
},
{
"col": 7,
"row": 6,
"code": "AL",
"state": "Alabama",
"total": 0
},
{
"col": 8,
"row": 6,
"code": "GA",
"state": "Georgia",
"total": 0
},
{
"col": 4,
"row": 7,
"code": "TX",
"state": "Texas",
"total": 0
},
{
"col": 9,
"row": 7,
"code": "FL",
"state": "Florida",
"total": 0
}
]
mobileTest = { if(/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|ipad|iris|kindle|Android|Silk|lge |maemo|midp|mmp|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i.test(navigator.userAgent)
|| /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(navigator.userAgent.substr(0,4))) { return true; } else { return false; }
};
map = [[0, 0, ""], [1, 0, ""], [2, 0, ""], [3, 0, ""], [4, 0, ""], [5, 0, ""], [6, 0, ""], [7, 0, ""], [8, 0, ""], [9, 0, ""], [10, 0, ""], [11, 0, "ME"], [0, 1, "AK"], [1, 1, ""], [2, 1, ""], [3, 1, ""], [4, 1, ""], [5, 1, ""], [6, 1, "WI"], [7, 1, ""], [8, 1, ""], [9, 1, ""], [10, 1, "VT"], [11, 1, "NH"], [0, 2, ""], [1, 2, "WA"], [2, 2, "ID"], [3, 2, "MT"], [4, 2, "ND"], [5, 2, "MN"], [6, 2, "IL"], [7, 2, "MI"], [8, 2, ""], [9, 2, "NY"], [10, 2, "MA"], [11, 2, ""], [0, 3, ""], [1, 3, "OR"], [2, 3, "NV"], [3, 3, "WY"], [4, 3, "SD"], [5, 3, "IA"], [6, 3, "IN"], [7, 3, "OH"], [8, 3, "PA"], [9, 3, "NJ"], [10, 3, "CT"], [11, 3, "RI"], [0, 4, ""], [1, 4, "CA"], [2, 4, "UT"], [3, 4, "CO"], [4, 4, "NE"], [5, 4, "MO"], [6, 4, "KY"], [7, 4, "WV"], [8, 4, "VA"], [9, 4, "MD"], [10, 4, "DE"], [11, 4, ""], [0, 5, ""], [1, 5, ""], [2, 5, "AZ"], [3, 5, "NM"], [4, 5, "KS"], [5, 5, "AR"], [6, 5, "TN"], [7, 5, "NC"], [8, 5, "SC"], [9, 5, "DC"], [10, 5, ""], [11, 5, ""], [0, 6, "HI"], [1, 6, ""], [2, 6, ""], [3, 6, ""], [4, 6, "OK"], [5, 6, "LA"], [6, 6, "MS"], [7, 6, "AL"], [8, 6, "GA"], [9, 6, ""], [10, 6, ""], [11, 6, ""], [0, 7, ""], [1, 7, ""], [2, 7, ""], [3, 7, ""], [4, 7, "TX"], [5, 7, ""], [6, 7, ""], [7, 7, ""], [8, 7, ""], [9, 7, "FL"], [10, 7, ""], [11, 7, ""]].map(d => ({ col: d[0], row: d[1], code: d[2] }));
class GridMap {
constructor(container) {
this._container = container;
this._g = null;
this._width = 800;
this._height = 600;
this._style = {
transition: false,
shape: "square",
sizeByValue: false,
cr: 4,
defaultCellColor: "#999",
defaultTextColor: "white",
shortFormat: ".2s",
longFormat: ",.0d",
legendFormat: ",.0d",
legendTitle: "",
showOverlay: true,
showOverlayLegend: true,
alwaysShowOverlay: false,
showCellText: true,
hideCell: false,
overlayLegendThreshold: 14,
showMapLegend: true
};
this._cellPalette = d3.interpolateYlGnBu;
this._textPalette = d3.interpolateCubehelixDefault;
this._overlayPalette = d3.schemeTableau10;
this._gridData = null;
this._cols = 0;
this._rows = 0;
this._x = null;
this._y = null;
this._bandwidth = { x: 0, y: 0, hx: 0, hy: 0 };
this._data = null;
this._chartData = null;
this._field = {
code: "code",
name: "state",
values: [],
total: ""
};
this._contains = { data: true, values: true };
this._c = null; // cell color scale
this._t = null; // text color scale
this._ov = null; // overlay mini chart color scale
this._fullOpacity = 1;
this._showLabel = true;
this._initDuration = 500;
this._miniLegend = null;
this._mapLegend = null;
this._customSquareOverlay = null;
this._customCircleOverlay = null;
this._onMouseEnter = null;
this._onMouseLeave = null;
this._onClick = null;
this._uniqueId = new String(Date.now() * Math.random()).replace(".", "");
}
size(_) {
return arguments.length ? (this._width = _[0], this._height = _[1], this) : [this._width, this._height];
}
style(_) {
return arguments.length ? (this._style = Object.assign(this._style, _), this) : this._style;
}
cellPalette(_) {
return arguments.length ? (this._cellPalette = _, this) : this._cellPalette;
}
overlayPalette(_) {
return arguments.length ? (this._overlayPalette = _, this) : this._overlayPalette;
}
mapGrid(_) {
return arguments.length ? (this._gridData = _, this) : this._gridData;
}
data(_) {
return arguments.length ? (this._data = _, this) : this._data;
}
field(_) {
return arguments.length ? (this._field = Object.assign(this._field, _), this) : this._field;
}
customSquareOverlay(_) {
return arguments.length ? (this._customSquareOverlay = _, this) : this._customSquareOverlay;
}
customCircleOverlay(_) {
return arguments.length ? (this._customCircleOverlay = _, this) : this._customCircleOverlay;
}
onMouseEnter(_) {
return arguments.length ? (this._onMouseEnter = _, this) : this._onMouseEnter;
}
onMouseLeave(_) {
return arguments.length ? (this._onMouseLeave = _, this) : this._onMouseLeave;
}
onClick(_) {
return arguments.length ? (this._onClick = _, this) : this._onClick;
}
get unitSize() {
const style = this._style, bandwidth = this._bandwidth;
const unit = Math.min(bandwidth.x, bandwidth.y);
return style.sizeByValue ? unit / 2 : unit / (style.shape === "circle" ? 2 : 1);
}
get actualSize() {
const u = this.unitSize;
return [this._cols * u, this._rows * u];
}
render() {
this._init();
this._process();
this._initColors();
this._g = this._container.append("g");
this._renderMap();
return this;
}
process() {
this._init();
this._process();
return this._chartData;
}
processMap() {
this._gridData = this._gridData.filter(d => d.code !== "");
this._cols = d3.max(this._gridData.map(d => d.col)) + 1;
this._rows = d3.max(this._gridData.map(d => d.row)) + 1;
this._x = d3.scaleBand()
.domain(this._seq(this._cols))
.padding(0.05);
this._y = d3.scaleBand()
.domain(this._seq(this._rows))
.padding(0.05);
const top = this._style.showMapLegend ? 50 : 0;
if (this._width / this._cols < this._height / this._rows) {
this._x.range([0, this._width - top]);
this._y.range([top, this._width / this._cols * this._rows]);
}
else {
this._x.range([0, this._height / this._rows * this._cols - top]);
this._y.range([top, this._height]);
}
this._bandwidth = {
x: this._x.bandwidth(),
y: this._y.bandwidth(),
hx: this._x.bandwidth() / 2,
hy: this._y.bandwidth() / 2
};
return this;
}
_init() {
this._contains.data = this._data && this._data.length > 0;
this._contains.values = this._field.values && this._field.values.length > 0;
if (!this._contains.data) {
this._style.showMapLegend = false;
this._style.sizeByValue = false;
this._style.showOverlay = false;
this._style.showOverlayLegend = false;
}
else if (!this._contains.values) {
this._style.showOverlay = false;
this._style.showOverlayLegend = false;
}
}
_process() {
this.processMap();
if (this._data && this._data.length > 0) this._processData();
}
_processData() {
const field = this._field;
this._showLabel = false;
this._chartData = this._gridData.map(d => {
const datum = this._data.find(_ => _[field.code] === d.code);
const values = datum && this._contains.values ? field.values.map(vname => datum[vname]) : [];
const r = {
col: d.col,
row: d.row,
code: d.code,
state: datum ? datum[field.name] : null,
values: values,
total: 0
};
if (r.state && r.state !== "") this._showLabel = true;
if (datum) {
if (field.total !== "")
r.total = datum[field.total];
else if (values)
r.total = values.reduce((a, b) => a + b);
}
return r;
})
}
_initColors() {
if (this._chartData) {
const ext = d3.extent(this._chartData.map(d => d.total));
this._c = d3.scaleSequential(this._cellPalette).domain(ext);
this._t = d3.scaleSequential(this._textPalette).domain(ext);
if (this._contains.values) this._ov = d3.scaleOrdinal(this._overlayPalette).domain(this._seq(this._field.values.length));
}
}
_renderMap() {
const style = this._style;
d3.select("body")
.append("style")
.html(`
._overlay_${this._uniqueId} { opacity: 0 }
.overlay_${this._uniqueId} {opacity:${style.alwaysShowOverlay ? 1 : 0}}
.overlay_${this._uniqueId}:hover {opacity:1}`
);
this._fullOpacity = style.sizeByValue ? 0.9 : 1;
const cells = this._renderBase().call(g => this._renderShape(g));
if (style.transition) {
const b = this._initDuration / this._cols;
cells.transition().duration(d => d.col * b)
.ease(d3.easeBounce)
.attr("opacity", this._fullOpacity)
.attr("transform", d => `translate(${this._x(d.col)},${this._y(d.row)})`)
// avoid interfering with the initial transition by delaying to attach mouse events
.transition().delay(this._initDuration)
.on("end", () => { this._attachEvents(cells); });
}
else
this._attachEvents(cells);
this._miniLegend = this._container
.append("g")
.style("font-weight", "bold")
.style("visibliity", "hidden");
if (style.showMapLegend && this._chartData) this._addMapLegend(cells);
}
_renderBase() {
var data;
if (this._chartData)
data = this._style.sizeByValue ? this._pack() : this._chartData;
else
data = this._gridData;
const cells = this._g.selectAll("g")
.data(data)
.join("g")
.attr("text-anchor", "middle")
.attr("opacity", this._style.transition ? 0 : this._fullOpacity)
.attr("fill", d => d.total ? this._c(d.total) : d.code ? this._style.defaultCellColor : "none");
if (this._style.transition)
cells.attr("transform", d => `translate(${-this._bandwidth.x},${this._y(d.row)})`);
else
cells.attr("transform", d => `translate(${this._x(d.col)},${this._y(d.row)})`);
return cells;
}
_renderShape(g) {
const defaultSize = this.unitSize;
if (this._style.shape === "square")
this._renderSquares(g, defaultSize)
else
this._renderCircles(g, defaultSize);
}
_renderSquares(g, size) {
const style = this._style, bandwidth = this._bandwidth;
const drawRect = g => {
const rect = g.append("rect")
.attr("rx", style.cr).attr("ry", style.cr);
if (style.sizeByValue) {
rect.attr("x", d => -d.r + size)
.attr("y", d => -d.r + size)
.attr("width", d => d.r * 2)
.attr("height", d => d.r * 2);
}
else {
rect.attr("width", size)
.attr("height", size);
}
return rect;
}
if (!style.hideCell) {
drawRect(g);
this._addCellText(g, true, "ctext");
}
const og = g.filter(d => d.total)
.append("g")
.attr("class", `_overlay_${this._uniqueId}`)
.attr("transform", d => style.sizeByValue ? `translate(${-(d.r - size)},${-(d.r - size)})` : "")
.call(g => drawRect(g).attr("opacity", 0)) // hidden layer for hovering
.call(g => g.append("g")
.attr("font-weight", "bold")
.attr("transform", d => style.sizeByValue ?
`translate(${d.r - size},${d.r * 2 - size + 15})` :
`translate(0,${bandwidth.hx + 15})`)
.call(g => {
if (style.alwaysShowOverlay)
this._addCellText(g, false, "ltext", 0);
else
this._addCellText(g);
}));
if (style.showOverlay) {
if (this._customSquareOverlay) {
og.call(g => {
const getSize = style.sizeByValue ? d => d.r * 2 : d => size;
this._customSquareOverlay(g, getSize, this._ov);
});
}
else {
og.append("g")
.selectAll("rect")
.data(d => this._treemap(d))
.join("rect")
.attr("x", d => d.x0).attr("y", d => d.y0)
.attr("width", d => d.x1 - d.x0).attr("height", d => d.y1 - d.y0)
//.attr("fill", (d, i) => this._ov(this._field.values[i]))
.attr("fill", (d, i) => this._ov(i))
.call(g => g.append("title")
.text((d, i) => {
const val = d.parent ? d.parent.data.children[i] : 0,
p = d.parent ? (val / d.parent.value * 100).toFixed(1) : 0;
return `${this._field.values[i]}\n${d3.format(this._style.legendFormat)(val)} (${p}%)`;
}));
}
}
}
_renderCircles(g, size) {
const style = this._style, bandwidth = this._bandwidth;
const drawCircle = (g, shift) => {
const circle = g.append("circle")
.attr("cx", size - shift ? 0 : bandwidth.hx).attr("cy", size - shift ? 0 : bandwidth.hy);
if (style.sizeByValue)
circle.attr("r", d => d.r);
else
circle.attr("r", d => size);
return circle;
}
if (!style.hideCell) {
drawCircle(g);
this._addCellText(g, true, "ctext");
}
const og = g.filter(d => d.total)
.append("g")
.attr("class", `_overlay_${this._uniqueId}`)
.attr("transform", `translate(${bandwidth.hx},${bandwidth.hy})`)
.call(g => drawCircle(g, true).attr("opacity", 0)) // hidden layer for hovering
.call(g => g.append("g")
.attr("font-weight", "bold")
.attr("transform", d => style.sizeByValue ?
`translate(${-bandwidth.hx},${d.r - bandwidth.hy + 15})` :
`translate(${-bandwidth.hx},${size - bandwidth.hy + 15})`)
.call(g => {
if (style.alwaysShowOverlay)
this._addCellText(g, false, "ltext", 0);
else
this._addCellText(g);
}));
if (this._style.showOverlay) {
if (this._customCircleOverlay) {
og.call(g => {
const getSize = style.sizeByValue ? d => d.r * 2 : d => size;
this._customCircleOverlay(g, getSize, this._ov);
});
}
else {
og.append("g")
.selectAll("path")
.data(d => d3.pie()(d.values).map(p => ({ pie: p, data: d })))
.join("path")
.attr("class", "mini")
//.attr("fill", (d, i) => this._ov(this._field.values[i]))
.attr("fill", (d, i) => this._ov(i))
.attr("d", d => d3.arc()
.innerRadius(0)
.outerRadius(style.sizeByValue ? d.data.r : size)
.startAngle(d.pie.startAngle)
.endAngle(d.pie.endAngle)())
.call(g => g.append("title")
.text((d, i) => {
const val = d.data.values[i], p = (val / d.data.total * 100).toFixed(1);
return `${this._field.values[i]}\n${d3.format(this._style.legendFormat)(val)} (${p}%)`;
}));
}
}
}
_addCellText(g, short, className, opacity) {
if (!this._style.showCellText) return;
const tg = g.append("g")
.attr("class", className)
.attr("opacity", opacity)
.attr("fill", d => d.total ? short ? this._t(d.total) : "black" : this._style.defaultTextColor)
.attr("transform", `translate(${this._bandwidth.hx},${this._bandwidth.hy})`);
if (short || this._showLabel) tg.call(g => g.append("text").text(d => short ? d.code : d.state));
tg.call(g => g.append("text")
.attr("dy", short || this._showLabel ? "1em" : "")
.text(d => d.total ? d3.format(short ? this._style.shortFormat : this._style.longFormat)(d.total) : "---"));
}
_attachEvents(cells) {
cells
.on("mouseenter", (e, d) => {
e.currentTarget.parentElement.appendChild(e.currentTarget);
cells.transition().duration(500)
.attr("opacity", a => a === d ? 1 : 0.4);
//.selectAll(".ctext").attr("opacity", 0.3);
if (this._style.alwaysShowOverlay)
cells.selectAll(".ltext").attr("opacity", a => a === d ? 1 : 0);
if (d.total && d.values) this._showLegend(d);
if (this._onMouseEnter) this._onMouseEnter(e, d);
})
.on("mouseleave", (e, d) => {
cells.transition().duration(500)
.attr("opacity", this._fullOpacity);
//.selectAll(".ctext").attr("opacity", 1);
if (this._style.alwaysShowOverlay)
cells.selectAll(".ltext").attr("opacity", 0);
this._miniLegend.style("visibility", "hidden");
if (this._onMouseLeave) this._onMouseLeave(e, d);
})
.on("click", (e, d) => {
if (this._onClick) this._onClick(e, d);
});
cells.selectAll(`._overlay_${this._uniqueId}`).attr("class", `overlay_${this._uniqueId}`);
}
_showLegend(d) {
const style = this._style, bandwidth = this._bandwidth;
if (!style.showOverlay || this._field.values.length > style.overlayLegendThreshold) return;
const w = 20, h = 12;
var r, left, top;
if (style.sizeByValue) {
r = d.r;
left = this._x(d.col) + bandwidth.x + 10 + (d.r - bandwidth.hx);
top = this._y(d.row);
}
else {
r = bandwidth.hx;
left = this._x(d.col) + bandwidth.x + 10;
top = this._y(d.row);
}
const getText = (_, i) => {
const val = d.values[i], p = (val / d.total * 100).toFixed(1);
return `${this._field.values[i]} ${d3.format(style.legendFormat)(val)}(${p}%)`;
};
this._miniLegend
.attr("transform", `translate(${left},${top})`)
.selectAll("g")
.data(this._field.values.slice(0, style.overlayLegendThreshold))
.join(
enter => {
return enter.append("g")
.attr("transform", (_, i) => `translate(0,${(h + 5) * i})`)
.call(g => g.append("rect")
.attr("rx", 4).attr("ry", 4)
.attr("width", w).attr("height", h)
//.attr("fill", (d, i) => this._ov(this._field.values[i])))
.attr("fill", (d, i) => this._ov(i)))
.call(g => g.append("text")
.attr("dy", "0.8em")
.attr("transform", `translate(${w + 5},0)`)
.text(getText))
},
update => update.select("text").text(getText),
exit => exit.remove());
this._miniLegend.node().parentElement.appendChild(this._miniLegend.style("visibility", "visible").node());
const bbox = this._miniLegend.node().getBBox();
if (left + bbox.width > this._width)
this._miniLegend.attr(
"transform",
`translate(${this._x(d.col) + bandwidth.hx - bbox.width - r - 10},${top})`);
}
_addMapLegend(cells) {
this._mapLegend = this._container.append("g")
.style("visibility", this._style.transition ? "hidden" : "visible")
.call(g => this._renderLegend(g)
.on("mouseover", (e, d) =>
cells.filter(c => c.total < d.floor || c.total >= d.ceiling)
.transition().duration(500)
.attr("opacity", 0.2))
.on("mouseout", () => cells.transition().duration(500).attr("opacity", 1)));
// avoid interfering with the initial transition
if (this._style.transition) {
this._mapLegend
.transition().delay(this._initDuration)
.style("visibility", "visible");
}
}
_renderLegend(g) {
const w = 30;
const s = sample(d3.extent(this._chartData.map(d => d.total).filter(d => d !== 0)), 8);
if (s.length > 0) {
g.attr("font-weight", "bold")
.append("text")
.attr("text-anchor", "end")
.attr("alignment-baseline", "hanging")
.attr("transform", `translate(${w * 9},0)`)
.text(this._style.legendTitle);
}
return g.selectAll("g")
.data(s)
.join("g")
.attr("font-size", "8pt")
.attr("transform", (d, i) => `translate(${i * w},0)`)
.call(g => g.append("rect")
.attr("y", "1.2em")
.attr("fill", d => this._c(d.floor))
.attr("width", w).attr("height", "1.5em"))
.call(g => g.append("text")
.attr("dy", "3.7em")
.text(d => d3.format(this._style.shortFormat)(d.floor)));
function sample(ext, segs) {
const min = ext[0], max = ext[1];
const gap = (max - min) / segs;
if (gap === 0) return [];
var curr = { floor: min, ceiling: 0 };
const s = [curr];
for (var i = min + gap; i <= max - gap; i += gap) {
const p = Math.pow(10, Math.round(i).toString().length - 2);
const v = Math.floor(i / p) * p;
curr.ceiling = v;
curr = { floor: v, ceiling: 0 };
s.push(curr);
}
curr.ceiling = max;
s.push({ floor: max, ceiling: Number.MAX_VALUE });
return s;
}
}
_pack() {
return d3.pack()
.size([this._width, this._height])(
d3.hierarchy({ children: this._chartData })
.sum(d => d.total))
.leaves()
.map(d => Object.assign(d, d.data));
}
_treemap(d) {
const size = this._style.sizeByValue ? [d.r * 2, d.r * 2] : [this._bandwidth.x, this._bandwidth.y];
return d3.treemap()
.size(size)
(d3.hierarchy({ children: d.values }).sum(d => d))
.leaves();
}
_seq(length) {
const a = new Array(length);
for (let i = 0; i < length; i++) a[i] = i;
return a;
}
}