var mapCont = document.getElementById('map');
var margin = mapCont.offsetTop * 2;
mapCont.style.height = 'Calc(100vh - ' + margin + 'px)';
var abuseData = {
geoJsons: {
county:
{
layer: null,
data: null,
ready: false,
url: "./geojson/cb_2022_us_county_500k.geojson"
},
state:
{
layer: null,
data: null,
ready: false,
url: "./geojson/cb_2022_us_state_500k.geojson"
}
}
};
var clusteredMarkers;
var fnQueue = [];
var abuseDetailPanel;
var legendsHidden = {
detail: false,
data: false
};
var map = L.map('map', {
renderer: L.canvas()
}).setView([37.8, -96], 4);
map.createPane('labels');
map.getPane('labels').style.zIndex = 450;
map.getPane('labels').style.pointerEvents = 'none';
map.getPane('labels').style.opacity = .5;
map.createPane('terrainlines');
map.getPane('terrainlines').style.zIndex = 425;
map.getPane('terrainlines').style.pointerEvents = 'none';
map.getPane('terrainlines').style.opacity = .25;
var CartoDB_PositronNoLabels = L.tileLayer('https://{s}.basemaps.cartocdn.com/light_nolabels/{z}/{x}/{y}{r}.png', {
attribution: '© CARTO ' +
'Stadia Maps Stamen Design',
subdomains: 'abcd',
maxZoom: 20
}).addTo(map);
var Stadia_StamenTerrainLines = L.tileLayer('https://tiles.stadiamaps.com/tiles/stamen_terrain_lines/{z}/{x}/{y}{r}.{ext}', {
minZoom: 0,
maxZoom: 18,
ext: 'png',
pane: 'terrainlines'
}).addTo(map);
var CartoDB_VoyagerOnlyLabels = L.tileLayer('https://{s}.basemaps.cartocdn.com/rastertiles/voyager_only_labels/{z}/{x}/{y}{r}.png', {
subdomains: 'abcd',
maxZoom: 20,
pane: 'labels'
}).addTo(map);
function geoJsonsReady() {
var allReady = true;
Object.values(abuseData.geoJsons).forEach((g) => { if (!g.ready) allReady = false; });
return allReady;
}
function loadGeoJsons() {
var checkComplete = function (node) {
node.ready = true;
if (geoJsonsReady()) {
while (fnQueue.length > 0) {
let work = fnQueue.shift();
work.fnc.apply(null, work.arg);
}
}
};
Object.values(abuseData.geoJsons).forEach((gj) => {
fetch(gj.url).then(r => r.json()).then((d) => {
gj.data = d;
checkComplete(gj);
});
});
}
function zoomToFeature(e) {
map.fitBounds(e.target.getBounds());
}
function onEachFeature(feature, layer) {
if (feature.properties.incidents == 0) return;
var html;
if (feature.properties.NAMELSAD && feature.properties.incidents == 1) {
html = `${feature.properties.NAMELSAD}
1 incident`;
}
else if (feature.properties.NAMELSAD) {
html = `${feature.properties.NAMELSAD}
${feature.properties.incidents} incidents`;
}
else if (feature.properties.NAME && feature.properties.incidents == 1) {
html = `${feature.properties.NAME}
1 incident`;
}
else {
html = `${feature.properties.NAME}
${feature.properties.incidents} incidents`;
}
layer.bindTooltip(html, { interactive: false, sticky: true });
layer.on('click', zoomToFeature);
}
function addLegendShowHideHandlers() {
var legends = document.getElementsByClassName('legendTitle');
if (!legends || legends.length == 0) return;
Object.values(legends).forEach((elm, i) => {
elm.addEventListener('click', handleLegendShowHide);
});
}
function removeLegendShowHideHandlers() {
var legends = document.getElementsByClassName('legendTitle');
if (!legends || legends.length == 0) return;
Object.values(legends).forEach((elm, i) => {
elm.removeEventListener('click', handleLegendShowHide);
});
}
function handleLegendShowHide(e) {
/*if (e.target.classList.contains('hidden')) {
var sbl = e.target.nextElementSibling;
while (sbl) {
var dsp = sbl.getAttribute('restoreDisplay') || 'block';
sbl.style.display = dsp;
sbl = sbl.nextElementSibling;
}
e.target.classList.remove('hidden');
}
else {
var sbl = e.target.nextElementSibling;
while (sbl) {
var dsp = sbl.style.display || 'block';
sbl.setAttribute('restoreDisplay', dsp);
sbl.style.display = 'none';
sbl = sbl.nextElementSibling;
}
e.target.classList.add('hidden');
}*/
var sbl = e.target.nextElementSibling;
while (sbl) {
if (sbl.classList.contains('hidden')) {
sbl.classList.remove('hidden');
}
else {
sbl.classList.add('hidden');
}
sbl = sbl.nextElementSibling;
}
if (e.target.classList.contains('hidden')) {
var lg = e.target.getAttribute('lg');
legendsHidden[lg] = false;
}
else {
e.target.classList.add('hidden');
var lg = e.target.getAttribute('lg');
legendsHidden[lg] = true;
}
return false;
}
function showBoundaryLayer() {
if (!geoJsonsReady()) return;
removeLegendShowHideHandlers();
var z = map.getZoom();
if (z >= 8.5) {
if (map.hasLayer(abuseData.geoJsons.state.layer)) abuseData.geoJsons.state.layer.remove();
if (map.hasLayer(abuseData.geoJsons.county.layer)) abuseData.geoJsons.county.layer.remove();
if (clusteredMarkers && !map.hasLayer(clusteredMarkers))
map.addLayer(clusteredMarkers);
if (abuseDetailPanel && !map.hasLayer(abuseDetailPanel))
abuseDetailPanel.addTo(map);
}
else if (z > 4) {
if (map.hasLayer(abuseData.geoJsons.state.layer)) abuseData.geoJsons.state.layer.remove();
if (!map.hasLayer(abuseData.geoJsons.county.layer)) abuseData.geoJsons.county.layer.addTo(map);
if (clusteredMarkers && !map.hasLayer(clusteredMarkers))
map.addLayer(clusteredMarkers);
if (abuseDetailPanel && !map.hasLayer(abuseDetailPanel))
abuseDetailPanel.addTo(map);
}
else {
if (map.hasLayer(abuseData.geoJsons.county.layer)) abuseData.geoJsons.county.layer.remove();
if (!map.hasLayer(abuseData.geoJsons.state.layer)) abuseData.geoJsons.state.layer.addTo(map);
if (clusteredMarkers && !map.hasLayer(clusteredMarkers))
map.addLayer(clusteredMarkers);
if (abuseDetailPanel && !map.hasLayer(abuseDetailPanel))
abuseDetailPanel.addTo(map);
}
addLegendShowHideHandlers();
}
function receiveDataLayers(layers, catDetails) {
if (!geoJsonsReady()) {
fnQueue.push({ fnc: receiveDataLayers, arg: [layers, catDetails] });
return;
}
clearDataLayers();
var catValueColors = [];
var legendItems = {
title: '', catValues: {}
};
if (catDetails) {
catValueColors = (chroma.scale('Paired').colors(catDetails.length)).reverse();
catValueColors.forEach((c, i) => {
if (c == '#ffffd9')
catValueColors[i] = '#858585';
});
legendItems.title = `${Object.keys(catDetails[0])[0]} Distribution`;
catDetails.forEach((c, idx) => {
var items = Object.values(c);
legendItems.catValues[items[0]] = catValueColors[idx];
});
}
else {
catValueColors = (chroma.scale('Paired').colors(1)).reverse();
catValueColors.forEach((c, i) => {
if (c == '#ffffd9')
catValueColors[i] = '#858585';
});
legendItems = {
title: `All Incidents`, catValues: { 'All Locations': catValueColors[0] }
};
}
abuseDetailPanel.onAdd = function (map) {
var div = L.DomUtil.create('div', 'info detailPanel');
var legendKey = "";
Object.entries(legendItems.catValues).forEach((e, i) => {
legendKey += '
location: ${(layers[i]['Incident City'] ? layers[i]['Incident City'] + ', ' : '') + (layers[i]['Incident County'] ? layers[i]['Incident County'] + ', ' : '') + (layers[i]['Incident State'] ? layers[i]['Incident State'] : '')}
`; html += `# of children affected: ${layers[i]['Child Number']}
`; html += `year of incident: ${!layers[i]['Incident Year'] || layers[i]['Incident Year'] == 0 ? 'not available' : layers[i]['Incident Year']}
`; html += `distribution: ${o['categoryValue']}
`; var marker = L.circleMarker(new L.LatLng(lat, lng), { title: title, pane: 'markerPane', color: '#000000', weight: 1, opacity: .3, fill: true, fillOpacity: 1, fillColor: o['catValueColor'] }); marker['incidentGroup'] = o; marker.bindPopup(html); markers.push(marker); if (layers[i]['Incident State GEO_ID']) { var ft = abuseData.geoJsons.state.data.features.find((f) => f.properties.GEOID == layers[i]['Incident State GEO_ID']); if (ft) { ft.properties.incidents += 1; } } if (layers[i]['Incident County GEO_ID']) { var ft = abuseData.geoJsons.county.data.features.find((f) => f.properties.GEOID == layers[i]['Incident County GEO_ID']); if (ft) { ft.properties.incidents += 1; } } } clusteredMarkers.addLayers(markers, { pane: 'terrainlinesPane' }).addTo(map); abuseDetailPanel.addTo(map); var bins = abuseData.geoJsons.county.data.features.filter((f) => f.properties.incidents > 0); abuseData.geoJsons.county.layer = L.dataClassification(abuseData.geoJsons.county.data, { style: { color: "rgba(233, 234, 235, .5)" }, mode: 'jenks', classes: bins.length >= 4 ? 5 : 3, field: 'incidents', pointMode: 'color', colorRamp: 'YlOrRd', classRounding: 0, legendTitle: 'Incidents by County', legendPosition: 'bottomright', onEachFeature: onEachFeature }); bins = abuseData.geoJsons.state.data.features.filter((f) => f.properties.incidents > 0); abuseData.geoJsons.state.layer = L.dataClassification(abuseData.geoJsons.state.data, { style: { color: "rgba(233, 234, 235, .5)" }, mode: 'jenks', classes: bins.length >= 4 ? 5 : 3, field: 'incidents', pointMode: 'color', colorRamp: 'YlOrRd', classRounding: 0, legendTitle: 'Incidents by State', legendPosition: 'bottomright', onEachFeature: onEachFeature }); map.on('zoomend', showBoundaryLayer); if (!catDetails) { if (map.getZoom() == 3) showBoundaryLayer(); map.setView([37.8, -96], 3); return; } showBoundaryLayer(); } function createClusterIcon(cluster) { var markers = cluster.getAllChildMarkers(); var html = ''; var n = 0; var objColors = [] for (var i = 0; i < markers.length; i++) { //keep it to just the one selected category for now var o = markers[i]['incidentGroup']; var catValue = o['categoryValue']; n += Array.isArray(catValue) ? catValue.length : 1; if (objColors.indexOf(o['catValueColor']) < 0) objColors.push(o['catValueColor']); } var wedge = 360 / objColors.length; var pieCSS = []; var startDeg = 0; for (var i = 0; i < objColors.length; i++) { pieCSS.push(`${objColors[i]} ${(wedge * i)}deg, ${objColors[i]} ${((i + 1) * wedge)}deg`); } var html = `