viewof scenarioInputs = form(
html`
<form style="text-align: center; font-size: 16px; margin-bottom: 5px;">
<label class="first-radio-button">
<input id="first-radio-button" type="radio" name="optradio" value="0" checked>Cities & Towns
</label>
<label class="second-radio-button">
<input id="second-radio-button" type="radio" name="optradio" value="1">Counties
</label>
</form>
`
)
viewof filterInputs = form(
html`
<form style="text-align: center; font-size: 16px; margin-bottom: 5px;">
<label class="second-filter-button">
<input id="second-filter-button" type="radio" name="optradio" value="0.05" checked>5%
</label>
<label class="third-filter-button">
<input id="third-filter-button" type="radio" name="optradio" value="0.1">10%
</label>
<label class="fourth-filter-button">
<input id="fourth-filter-button" type="radio" name="optradio" value="0.15">15%
</label>
<label class="fifth-filter-button">
<input id="fifth-filter-button" type="radio" name="optradio" value="0.25">25%
</label>
<label class="sixth-filter-button">
<input id="sixth-filter-button" type="radio" name="optradio" value="0.5">50%
</label>
</form>
`
)
html`
<div>
<div style="width: 300px; padding: 5px; font-weight: 700; position: fixed; display: inline-block; left: 0; right: auto;">${viewof filterInputs}</div>
<div style="width: 250px; padding: 5px; font-weight: 700; position: fixed; display: inline-block; right: auto; right: 0;">${viewof scenarioInputs}</div>
</div>
`
d3 = require("d3@5");
muniData = FileAttachment("fines_fees_cities_ii.json").json();
countyData = FileAttachment("fines_fees_counties_ii.json").json();
MapboxGeocoder = require("mapbox-gl-geocoder@2.0.1/dist/mapbox-gl-geocoder.min.js");
mapboxToken = "pk.eyJ1Ijoiam9yZGFuLXJlYXNvbiIsImEiOiJja21wYzBzeHgwMjV6MnZteDByMDFqM2I0In0.MLbmWpdibeG0GY-vHqxzDg";
mapboxgl = {
const mbgl = await require("https://api.mapbox.com/mapbox-gl-js/v2.9.2/mapbox-gl.js");
if (!mbgl.accessToken) {
mbgl.accessToken = mapboxToken;
const href = await require.resolve(
"https://api.mapbox.com/mapbox-gl-js/v2.9.2/mapbox-gl.css"
);
document.head.appendChild(html`<link href=${href} rel=stylesheet>`);
}
return mbgl;
};
html`<link rel='stylesheet' href='https://api.mapbox.com/mapbox-gl-js/plugins/mapbox-gl-geocoder/v2.3.0/mapbox-gl-geocoder.css' type='text/css' />`;
data = {
if (scenarioInputs.optradio == '0') {
return muniData
} else {
return countyData
}
};
fineFeesMap = {
let map = new mapboxgl.Map({
container,
// projection: 'globe',
center: [
-98,
38
],
zoom: 3.3,
style: 'mapbox://styles/jordan-reason/cld4wfcml002301nw869edr1g',
});
// Configure geocoder
let geocoder = new MapboxGeocoder({
accessToken: mapboxgl.accessToken,
country: 'us',
// bbox: [-125.35400390624999, 31.970803930433096, -113.26904296874999, 42.53689200787315]
});
map.addControl(geocoder, "top-left");
// Configure nav
let nav = new mapboxgl.NavigationControl();
map.addControl(nav, "top-right");
// yield new Promise(resolve => {
map.on('load', () => {
map.addSource("finesfees", {
type: "geojson",
data: data,
})
map.addLayer({
id: 'test',
type: 'circle',
paint: {
'circle-radius': {
'base': 1.9,
'stops': [
[6, 3],
[14, 100]
]
},
"circle-opacity": 0.9,
"circle-stroke-width": 0.25,
"circle-stroke-color": "#333",
'circle-color': {
property: 'PercentGenRev',
stops: [
[0, '#ed1107'],
[0.8, '#ed1107']
]
},
},
filter: ['>', 'PercentGenRev', +filterInputs.optradio],
source: "finesfees",
})
// map.setFilter('test', ['>', 'PercentGenRev', y])
map.popup = new mapboxgl.Popup({
closeButton: false,
closeOnClick: false,
})
map.on('mouseenter', 'test', function(e) {
map.getCanvas().style.cursor = 'pointer'
var coordinates = e.features[0].geometry.coordinates.slice()
const { Name, State, Population, PercentGenRev, GeneralRevenue, FinesForfeits } = e.features[0].properties
while (Math.abs(e.lngLat.lng - coordinates[0]) > 180) {
coordinates[0] += e.lngLat.lng > coordinates[0] ? 360 : -360
}
var htmlContent = `
<span style="font-family: 'Open Sans', sans-serif; font-weight: 500;">
<span style="font-size: 18px; font-weight: 700;"><span class="capitalize">${Name}</span></span>
<hr style='margin:2px; padding:0; border-top: 3px solid #d3d3d3;'>
Fines & Forfeits: ${d3.format("$,.0f")(FinesForfeits * 1e3)}<br>
<hr style='margin:1px; padding:0; border-top: 1px solid #d3d3d3;'>
% of Revenue: ${d3.format(".1%")(FinesForfeits / GeneralRevenue)}<br>
<hr style='margin:1px; padding:0; border-top: 1px solid #d3d3d3;'>
General Revenue: ${d3.format("$,.0f")(GeneralRevenue * 1e3)}<br>
<hr style='margin:1px; padding:0; border-top: 1px solid #d3d3d3;'>
Per Capita: ${d3.format("$,.0f")((FinesForfeits * 1e3)/Population)}
</span>
`
map.popup.setLngLat(coordinates)
.setHTML(htmlContent)
.addTo(map)
})
})
map.on('mouseleave','test', function() {
map.getCanvas().style.cursor = ''
map.popup.remove()
})
// return resolve(map)
return map;
// })
}
scenario1ButtonColor = {
if (scenarioInputs.optradio == '0') {
return "#d3d3d3"
} else {
return "#fff"
}
};
scenario2ButtonColor = {
if (scenarioInputs.optradio == '1') {
return "#d3d3d3"
} else {
return "#fff"
}
};
filter1ButtonColor = {
if (filterInputs.optradio == '0') {
return "#d3d3d3"
} else {
return "#fff"
}
};
filter2ButtonColor = {
if (filterInputs.optradio == '0.05') {
return "#d3d3d3"
} else {
return "#fff"
}
};
filter3ButtonColor = {
if (filterInputs.optradio == '0.1') {
return "#d3d3d3"
} else {
return "#fff"
}
};
filter4ButtonColor = {
if (filterInputs.optradio == '0.15') {
return "#d3d3d3"
} else {
return "#fff"
}
};
filter5ButtonColor = {
if (filterInputs.optradio == '0.25') {
return "#d3d3d3"
} else {
return "#fff"
}
};
filter6ButtonColor = {
if (filterInputs.optradio == '0.5') {
return "#d3d3d3"
} else {
return "#fff"
}
};
html`
<style>
#first-radio-button,
#second-radio-button,
#first-filter-button,
#second-filter-button,
#third-filter-button,
#fourth-filter-button,
#fifth-filter-button,
#sixth-filter-button {
display: none;
}
.first-radio-button {
background-color: ${scenario1ButtonColor};
border-radius: 5px 0 0 5px;
border: 1px solid #000;
padding: 0 5px 0 5px;
}
.second-radio-button {
background-color: ${scenario2ButtonColor};
border-radius: 0 5px 5px 0;
border: 1px solid #000;
padding: 0 5px 0 5px;
}
.first-filter-button {
background-color: ${filter1ButtonColor};
border-radius: 5px 0 0 5px;
border: 1px solid #000;
padding: 0 5px 0 5px;
}
.second-filter-button {
background-color: ${filter2ButtonColor};
border-radius: 5px 0 0 5px;
border: 1px solid #000;
padding: 0 5px 0 5px;
}
.third-filter-button {
background-color: ${filter3ButtonColor};
border-radius: 0;
border: 1px solid #000;
padding: 0 5px 0 5px;
}
.fourth-filter-button {
background-color: ${filter4ButtonColor};
border-radius: 0;
border: 1px solid #000;
padding: 0 5px 0 5px;
}
.fifth-filter-button {
background-color: ${filter5ButtonColor};
border-radius: 0;
border: 1px solid #000;
padding: 0 5px 0 5px;
}
.sixth-filter-button {
background-color: ${filter6ButtonColor};
border-radius: 0 5px 5px 0;
border: 1px solid #000;
padding: 0 5px 0 5px;
}
`
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
}