I have a button that toggles data sets, along with toggling that dataset, I would like it to update the colorbar title and the colorscale, but it will change once, and then it seems to get “hung up” how would I refresh the plotly map in order to change the title and colorscale back, when I press the button again.
let isOn = true;
let title = 'GDP';
let csvStates, counts, gdpData, incomeData;
//async function, needs to be async so I can use fetch.
async function setup() {
await d3.csv('Companies.csv', d3.AutoType) //this pulls in the csv data d3.AutoType auto converts string values to appropriate types but im not sure if i really need this, but the d3 tutorial guy said I should keep it
.then(function(rows){ //d3 runs on a "promise chain" nothing happens unless the csv actually loads in
rows.forEach(row => { //logs all the zip_code rows so I know what I got. (sifts thru each row)
});
//this here will pass all the data
let result = getZipCode(rows);
csvStates = result.csvStates; // Assign to outer scope variables
counts = result.counts;
});
const apiKey = "7E8EAAB4-A2AE-42D6-99E5-A9DBE54DEF89";
const gdpUrl = `https://apps.bea.gov/api/data/?UserID=${apiKey}&method=GetData&datasetname=Regional&TableName=CAGDP1&LineCode=1&GeoFIPS=STATE&Year=2022&ResultFormat=JSON`; //this isnt the "basic" URL year is not all because then there was like 100 things in my array. dont fuck with much besides datasetname
const gdpResponse = await fetch(gdpUrl); //need to use fetch to grab data
const gdpDatas = await gdpResponse.json();
console.log(gdpDatas); //see if I got anything!
const allGdpData = gdpDatas.BEAAPI.Results.Data;
console.log(allGdpData); // Look at what the data looks like cuz i cant grab states
//ok this just takes everything out manually
gdpData = allGdpData.filter(s =>
s.GeoName !== 'United States' &&
s.GeoName !== 'New England' &&
s.GeoName !== 'Mideast' &&
s.GeoName !== 'Great Lakes' &&
s.GeoName !== 'Plains' &&
s.GeoName !== 'Southeast' &&
s.GeoName !== 'Southwest' &&
s.GeoName !== 'Rocky Mountain' &&
s.GeoName !== 'Far West'
);
//Do this a second time for income data
const incomeUrl = `https://apps.bea.gov/api/data/?UserID=${apiKey}&method=GetData&datasetname=Regional&TableName=SAINC1&LineCode=3&GeoFIPS=STATE&Year=2022&ResultFormat=JSON`;
const incomeResponse = await fetch(incomeUrl); //need to use fetch to grab data
const incomeDatas = await incomeResponse.json();
console.log(incomeDatas); //see if I got anything!
const allincomeData = incomeDatas.BEAAPI.Results.Data;
console.log(allincomeData); // Look at what the data looks like cuz i cant grab states
//ok this just takes everything out manually
incomeData = allincomeData.filter(s =>
s.GeoName !== 'United States' &&
s.GeoName !== 'New England' &&
s.GeoName !== 'Mideast' &&
s.GeoName !== 'Great Lakes' &&
s.GeoName !== 'Plains' &&
s.GeoName !== 'Southeast' &&
s.GeoName !== 'Southwest' &&
s.GeoName !== 'Rocky Mountain' &&
s.GeoName !== 'Far West'
);
let button = createButton(title);
button.position(705, 415);
button.mousePressed(() => {
toggleData();
if (isOn) {
buttonText = 'GDP';
} else {
buttonText = 'Income';
}
button.html(buttonText); // Update the button
})
button.style('height', '30px')
.style('width', '80px')
.style('background-color', '#4CAF50')
.style('border', '0px')
.style('font-weight ', 'bold')
.style('color', 'white');
let helpbutton = createButton('Help Me');
helpbutton.position(705,450);
helpbutton.mousePressed(() => {
let helpBox = document.getElementById('helpBox'); //Gets the HTML element
if (helpBox.style.display === 'none' || helpBox.style.display === '') {
helpBox.style.display = 'block'; //makes this button actually VISIBLE/
} else {
helpBox.style.display = 'none';
}
})
helpbutton.style('height', '30px')
.style('width', '80px')
.style('border', '0px')
.style('font-weight ', 'bold')
.style('background-color', '#4CAF50')
.style('color', 'white');
createTheMap(gdpData, csvStates, counts, 'GDP'); // need to call this AFTER i get data.
}
function toggleData() {
isOn = !isOn;
if (isOn) {
createTheMap(gdpData, csvStates, counts);
} else {
createTheMap(incomeData, csvStates, counts);
}
}
function getZipCode(rows) { //
let stateCounts = {}; // need this to exist globally?
rows.forEach(row => { //sifts thru each row and snags zip code to store it.
let zip = row.Zip_Code; //for each row it goes to the Zip_Code header and adds it to variable zip
let state = getStateFromZip(zip);//pass thru current zipcode and save what state it is.
if (state) { //checks if the value is da thruth
if (!stateCounts[state]) stateCounts[state] = 0; // 0's it out if nothing
stateCounts[state]++; //adds one if somehting.
}
});
//make them arrays so I can actually plot them lol.
let csvStates = Object.keys(stateCounts);
let counts = Object.values(stateCounts);
return {csvStates, counts} //returns both arrays which are both returned from the function.
}
//stolen from claude, hope it works!
function getStateFromZip(zip) { //this function just changes all the zipcodes and returns the actual state they reside in.
let zipNum = parseInt(zip);
if (isNaN(zipNum)) return null;
// US ZIP code to state mapping
if (zipNum >= 35000 && zipNum <= 36999) return 'AL';
if (zipNum >= 99500 && zipNum <= 99999) return 'AK';
if (zipNum >= 85000 && zipNum <= 86999) return 'AZ';
if (zipNum >= 71600 && zipNum <= 72999) return 'AR';
if (zipNum >= 90000 && zipNum <= 96699) return 'CA';
if (zipNum >= 80000 && zipNum <= 81999) return 'CO';
if (zipNum >= 6000 && zipNum <= 6999) return 'CT';
if (zipNum >= 19700 && zipNum <= 19999) return 'DE';
if (zipNum >= 32000 && zipNum <= 34999) return 'FL';
if (zipNum >= 30000 && zipNum <= 31999) return 'GA';
if (zipNum >= 96700 && zipNum <= 96999) return 'HI';
if (zipNum >= 83200 && zipNum <= 83999) return 'ID';
if (zipNum >= 60000 && zipNum <= 62999) return 'IL';
if (zipNum >= 46000 && zipNum <= 47999) return 'IN';
if (zipNum >= 50000 && zipNum <= 52999) return 'IA';
if (zipNum >= 66000 && zipNum <= 67999) return 'KS';
if (zipNum >= 40000 && zipNum <= 42999) return 'KY';
if (zipNum >= 70000 && zipNum <= 71599) return 'LA';
if (zipNum >= 3900 && zipNum <= 4999) return 'ME';
if (zipNum >= 20600 && zipNum <= 21999) return 'MD';
if (zipNum >= 1000 && zipNum <= 2799) return 'MA';
if (zipNum >= 48000 && zipNum <= 49999) return 'MI';
if (zipNum >= 55000 && zipNum <= 56999) return 'MN';
if (zipNum >= 38600 && zipNum <= 39999) return 'MS';
if (zipNum >= 63000 && zipNum <= 65999) return 'MO';
if (zipNum >= 59000 && zipNum <= 59999) return 'MT';
if (zipNum >= 68000 && zipNum <= 69999) return 'NE';
if (zipNum >= 88900 && zipNum <= 89999) return 'NV';
if (zipNum >= 3000 && zipNum <= 3899) return 'NH';
if (zipNum >= 7000 && zipNum <= 8999) return 'NJ';
if (zipNum >= 87000 && zipNum <= 88499) return 'NM';
if (zipNum >= 10000 && zipNum <= 14999) return 'NY';
if (zipNum >= 27000 && zipNum <= 28999) return 'NC';
if (zipNum >= 58000 && zipNum <= 58999) return 'ND';
if (zipNum >= 43000 && zipNum <= 45999) return 'OH';
if (zipNum >= 73000 && zipNum <= 74999) return 'OK';
if (zipNum >= 97000 && zipNum <= 97999) return 'OR';
if (zipNum >= 15000 && zipNum <= 19699) return 'PA';
if (zipNum >= 2800 && zipNum <= 2999) return 'RI';
if (zipNum >= 29000 && zipNum <= 29999) return 'SC';
if (zipNum >= 57000 && zipNum <= 57999) return 'SD';
if (zipNum >= 37000 && zipNum <= 38599) return 'TN';
if (zipNum >= 75000 && zipNum <= 79999 || zipNum >= 88500 && zipNum <= 88599) return 'TX';
if (zipNum >= 84000 && zipNum <= 84999) return 'UT';
if (zipNum >= 5000 && zipNum <= 5999) return 'VT';
if (zipNum >= 20100 && zipNum <= 20599 || zipNum >= 22000 && zipNum <= 24699) return 'VA';
if (zipNum >= 98000 && zipNum <= 99499) return 'WA';
if (zipNum >= 24700 && zipNum <= 26999) return 'WV';
if (zipNum >= 53000 && zipNum <= 54999) return 'WI';
if (zipNum >= 82000 && zipNum <= 83199) return 'WY';
if (zipNum >= 20000 && zipNum <= 20099 || zipNum >= 20200 && zipNum <= 20599) return 'DC';
// return null;
}
function createTheMap(apiStates, csvStates, counts, dataType) {
//changes all the state names to thier abreviated versions using a look up table.
const stateNameToAbbr = { //this is a look up table
//if('01000') return 'AL';
'Alabama': 'AL', 'Alaska': 'AK', 'Arizona': 'AZ', 'Arkansas': 'AR',
'California': 'CA', 'Colorado': 'CO', 'Connecticut': 'CT', 'Delaware': 'DE',
'Florida': 'FL', 'Georgia': 'GA', 'Hawaii': 'HI', 'Idaho': 'ID',
'Illinois': 'IL', 'Indiana': 'IN', 'Iowa': 'IA', 'Kansas': 'KS',
'Kentucky': 'KY', 'Louisiana': 'LA', 'Maine': 'ME', 'Maryland': 'MD',
'Massachusetts': 'MA', 'Michigan': 'MI', 'Minnesota': 'MN', 'Mississippi': 'MS',
'Missouri': 'MO', 'Montana': 'MT', 'Nebraska': 'NE', 'Nevada': 'NV',
'New Hampshire': 'NH', 'New Jersey': 'NJ', 'New Mexico': 'NM', 'New York': 'NY',
'North Carolina': 'NC', 'North Dakota': 'ND', 'Ohio': 'OH', 'Oklahoma': 'OK',
'Oregon': 'OR', 'Pennsylvania': 'PA', 'Rhode Island': 'RI', 'South Carolina': 'SC',
'South Dakota': 'SD', 'Tennessee': 'TN', 'Texas': 'TX', 'Utah': 'UT',
'Vermont': 'VT', 'Virginia': 'VA', 'Washington': 'WA', 'West Virginia': 'WV',
'Wisconsin': 'WI', 'Wyoming': 'WY', 'District of Columbia': 'DC', 'Alaska *': 'AK', 'Hawaii *': 'HI'
};
//things change depending on data type section.
let valueLabel = dataType === 'GDP' ? 'Annual GDP: ' : 'Per Capita Income: ';
let colorscale = dataType === 'GDP' ? 'Viridis' : 'Greens';
let choropleth = [{
type: 'choropleth', //this is a specific type of basemap
locationmode: 'USA-states', //shows all the states on the US
locations: apiStates.map(s => stateNameToAbbr[s.GeoName]),
text: apiStates.map(s => s.GeoName),
z: apiStates.map(s => parseFloat(s.DataValue)),
colorscale: colorscale,
reversescale: true,
colorbar: {
title: {
text: dataType === 'GDP' ? 'GDP<br>(Millions $)' : 'Income<br>($ per capita)',
side: 'top',
//font: { color: '#ffffff', size: 12 }
},
hovertemplate: '<b>%{text}</b><br>' + // Shows full state name
valueLabel + '$%{z:,.0f}<br>' + // Shows value with label
'<extra></extra>', //removes trace thingy
x: dataType === 'GDP' ? 1.02 : 1.02 // Dummy change to force update
}
}];
let circles = {
type: 'scattergeo', //this is a specific type of basemap
locationmode: 'USA-states', //shows all the states on the US
locations: csvStates, //breaks up the US into state polys so that the choropleth can actually show up.
text: csvStates.map((s, i) => `${s}: ${counts[i]} companies`), //this sucker is stolen, but it helps with the pop up when you hover over a state, without it, it desplays in such a fashion that is too simple imo
hovertemplate: '<b>%{text}</b><br>' + // Shows full state name
'<extra></extra>', //removes trace thingy
marker: {
size: counts.map(c => 10 + c / 2 ), // adjust to control how big circles appear t
color: '#094D9C',
opacity: 0.6,
}
};
let layout = { //everything in here relates to the style of the map
title: 'Investment Geography: Company Locations vs. State Economic Metrics', //titles the map
geo: {
scope: 'usa', //Only shows the US instead of the entire world
},
width: 800,
height: 500,
};
Plotly.newPlot('myPlotlyChart', [choropleth[0], circles], layout); //this drops a new plot with the info gathared from choroplath, circles and layout.
}