-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
942679f
commit a549165
Showing
8 changed files
with
669 additions
and
247 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
/* | ||
* Leaflet.IconEx v1.1.0 - 2024-06-15 | ||
* | ||
* Copyright 2024 mfhsieh | ||
* [email protected] | ||
* | ||
* Licensed under the MIT license. | ||
* | ||
* Demos: | ||
* https://mfhsieh.github.io/leaflet-iconex/ | ||
* | ||
* Source: | ||
* [email protected]:mfhsieh/leaflet-iconex.git | ||
* | ||
*/ | ||
@-moz-document url-prefix() { | ||
|
||
/* .leaflet-div-icon div:last-child, | ||
.leaflet-iconex div:last-child, */ | ||
.leaflet-div-icon div:last-child .fa, | ||
.leaflet-div-icon div:last-child .fab, | ||
.leaflet-div-icon div:last-child .far, | ||
.leaflet-div-icon div:last-child .fas, | ||
.leaflet-iconex div:last-child .fa, | ||
.leaflet-iconex div:last-child .fab, | ||
.leaflet-iconex div:last-child .far, | ||
.leaflet-iconex div:last-child .fas { | ||
margin-top: .05rem; | ||
margin-bottom: -.05rem; | ||
} | ||
} | ||
|
||
.leaflet-div-icon { | ||
background: transparent; | ||
border: none; | ||
} | ||
|
||
.leaflet-div-icon, | ||
.leaflet-iconex { | ||
user-select: none; | ||
-webkit-user-select: none; | ||
-moz-user-select: none; | ||
-ms-user-select: none; | ||
-khtml-user-select: none; | ||
text-size-adjust: none; | ||
-webkit-text-size-adjust: none; | ||
-moz-text-size-adjust: none; | ||
-ms-text-size-adjust: none; | ||
-khtml-text-size-adjust: none; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,252 +1,12 @@ | ||
<!DOCTYPE html> | ||
<html lang="en"> | ||
<head> | ||
<base target="_top"> | ||
<meta charset="utf-8"> | ||
<meta name="viewport" content="width=device-width, initial-scale=1"> | ||
|
||
<title>Bloomberg Business Oportunity Alumni Map</title> | ||
|
||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.9.4/leaflet.css" integrity="sha512-Zcn6bjR/8RZbLEpLIeOwNtzREBAJnUKESxces60Mpoj+2okopSAcSUIUOseddDm0cxnGQzxIR7vJgsLZbdLE3w==" crossorigin="anonymous" referrerpolicy="no-referrer" /> | ||
<script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.9.4/leaflet.js" integrity="sha512-BwHfrr4c9kmRkLw6iXFdzcdWV/PGkVgiIyIWLLlTSXzWQzxuSg4DiQUCpauz/EWjgk5TYQqX/kvn9pG1NpYfqg==" crossorigin="anonymous" referrerpolicy="no-referrer"></script> | ||
<link rel="stylesheet" href="https://unpkg.com/[email protected]/dist/MarkerCluster.css"> | ||
<link rel="stylesheet" href="https://unpkg.com/[email protected]/dist/MarkerCluster.Default.css"> | ||
<script src="https://unpkg.com/[email protected]/dist/leaflet.markercluster.js"></script> | ||
<script src="https://unpkg.com/[email protected]/dist/leaflet.featuregroup.subgroup.js"></script> | ||
<script src="./leaflet.timeline.js"></script> | ||
<script src="https://cdn.jsdelivr.net/npm/d3@7"></script> | ||
<style> | ||
html, body { | ||
height: 100%; | ||
margin: 0; | ||
} | ||
.leaflet-container { | ||
height: 100vh; | ||
width: 100vw; | ||
max-width: 100%; | ||
max-height: 100%; | ||
} | ||
div.leaflet-control-layers { | ||
visibility: hidden; | ||
} | ||
div.legend { | ||
background-color: white; | ||
padding: 0 20px 20px; | ||
border-radius: 5px; | ||
box-shadow: 0 0 15px rgba(0,0,0,0.2); | ||
} | ||
/* div.legend div.entry { | ||
display: flex; | ||
justify-content: space-between; | ||
} | ||
div.legend.cluster div.entry { | ||
justify-content: flex-start; | ||
} | ||
div.activity div.entry div.swatch { | ||
background-color: rgba(219, 5, 5, 0.56); | ||
opacity: .5; | ||
margin-right: 30px; | ||
} | ||
div.cluster div.entry div.swatch { | ||
margin-right: 10px; | ||
width: 20px; | ||
height: 20px; | ||
border-radius: 10px; | ||
margin-bottom: 5px; | ||
pointer-events: none; | ||
} */ | ||
div.cluster h3 { | ||
max-width: 130px; | ||
} | ||
div.cluster div.progressGroup { | ||
display: flex; | ||
flex-direction: row; | ||
justify-content: space-around; | ||
} | ||
div.cluster div.progressGroup div.progress { | ||
width: 50px; | ||
height: auto; | ||
border-bottom: 1px solid black; | ||
border-top: 1px solid black; | ||
display: flex; | ||
flex-direction: column; | ||
gap: 0; | ||
} | ||
div.cluster div.progressGroup div.progress div.barTick { | ||
width: 30px; | ||
margin: auto; | ||
background: navy; | ||
flex-grow: 1; | ||
} | ||
div.cluster div.entry div.label { | ||
padding-top: 2px; | ||
pointer-events: none; | ||
} | ||
/* div.cluster div.entry div.label.reset { | ||
font-weight: 700; | ||
} */ | ||
</style> | ||
<meta charset="UTF-8"> | ||
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||
<title>10,000 Small Businesses Program Alumni Map</title> | ||
</head> | ||
<body> | ||
<div id="map" style="width: 100vw; height: 100vh;"></div> | ||
<script> | ||
|
||
const map = L.map('map').setView([38.47654, -76.71313], 8); | ||
const Stadia_AlidadeSmooth = L.tileLayer('https://tiles.stadiamaps.com/tiles/alidade_smooth/{z}/{x}/{y}{r}.{ext}', { | ||
minZoom: 0, | ||
maxZoom: 20, | ||
attribution: '© <a href="https://www.stadiamaps.com/" target="_blank">Stadia Maps</a> © <a href="https://openmaptiles.org/" target="_blank">OpenMapTiles</a> © <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors', | ||
ext: 'png' | ||
}).addTo(map); | ||
d3.csv("alumni.csv").then((data) => { | ||
console.log(data) | ||
// const activityScale = d3.scaleLinear().range([2, 16]) | ||
// const clusterScale = d3.scaleOrdinal(d3.schemeObservable10) | ||
// .domain(Array.from(new Set(data.features.map(m => m.properties.cluster_name))).sort((a,b)=>a.localeCompare(b))) | ||
// function pointToClusterLayer(feature, latlng) { | ||
// return L.circleMarker(latlng, { | ||
// radius: 6, | ||
// // fillColor: clusterScale(feature.properties.cluster_name), | ||
// fillOpacity: 1, | ||
// stroke: false | ||
// }) | ||
// } | ||
// function pointToActivityLayer(feature, latlng) { | ||
// return L.circleMarker(latlng, { | ||
// // radius: activityScale(+feature.properties.activity), | ||
// fillColor: "#db0505", | ||
// fillOpacity: .35, | ||
// stroke: true, | ||
// weight: 0.5, | ||
// color: "white" | ||
// }) | ||
// } | ||
const alumni = L.markerClusterGroup(); | ||
const subgroups = []; | ||
const gradDates = Array.from(new Set(data.map(d => d.end_date))); | ||
const barScale = d3.scaleLinear().range([0,1]) | ||
gradDates.forEach(cohort => { | ||
let endDate = cohort.split("/"); | ||
let gradDate = new Date(endDate[2], | ||
endDate[0].length < 2 ? `0${endDate[0]}` : endDate[0], | ||
endDate[1].length < 2 ? `0${endDate[1]}` : endDate[1]); | ||
let dateStrings = gradDate.toDateString().split(" "); | ||
let cohortLabel = `${dateStrings[1]} ${dateStrings[3]}`; | ||
let cohortData = data.filter(d => d.end_date === cohort); | ||
subgroups.push({ | ||
label: cohortLabel, | ||
data: cohortData, | ||
date: gradDate}); | ||
}) | ||
const subgroupsSorted = subgroups.slice().sort((a,b) => b.date - a.date) | ||
const dateScale = d3.scaleTime().domain([1,0]).range(subgroupsSorted.map(m => m.date)) | ||
|
||
subgroupsSorted.forEach(group => { | ||
let groupMarkers = []; | ||
group.data.forEach(alum => { | ||
groupMarkers.push( | ||
L.circleMarker([alum.latitude, alum.longitude], { | ||
radius: 10, | ||
fillColor: "#db0505", | ||
fillOpacity: .35, | ||
stroke: true, | ||
weight: 0.5, | ||
color: "white" | ||
}).bindTooltip(alum.business_name) | ||
) | ||
}) | ||
let cohortGroup = L.featureGroup.subGroup(alumni, groupMarkers) | ||
cohortGroup.addTo(map); | ||
group.layerGroup = cohortGroup; | ||
}) | ||
// const categories = L.geoJSON(data.features, { | ||
// pointToLayer: pointToClusterLayer | ||
// }).bindPopup(function(layer) { | ||
// return layer.feature.properties.stop_name; | ||
// }) | ||
// const activity = L.geoJSON(data.features, { | ||
// pointToLayer: pointToActivityLayer | ||
// }).bindPopup(function(layer) { | ||
// return layer.feature.properties.stop_name; | ||
// }) | ||
alumni.addTo(map) | ||
console.log(subgroupsSorted); | ||
const overlays = {} | ||
subgroupsSorted.forEach(g => overlays[g.label] = g.layerGroup) | ||
const layerControl = L.control.layers(null, overlays, {collapsed: false}).addTo(map); | ||
// const activityLegend = L.control({position: "bottomright"}) | ||
const clusterLegend = L.control({position: "bottomright"}) | ||
// activityLegend.onAdd = function(map) { | ||
// let div = L.DomUtil.create('div', 'legend activity'), | ||
// levels = [0, .25, .5, .75, 1]; | ||
// div.innerHTML += "<h3>Activity Levels</h3>"; | ||
// levels.forEach(level => { | ||
// let size = 2*activityScale(level); | ||
// div.innerHTML += `<div class="entry"> | ||
// <div class="swatch" style="width: ${size}px; height: ${size}px; border-radius: ${size/2}px; margin-bottom: ${30-size}px;" ></div> | ||
// <div class="label">${level}</div> | ||
// </div>` | ||
// }); | ||
// return div; | ||
// } | ||
// activityLegend.addTo(map); | ||
clusterLegend.onAdd = function(map) { | ||
let div = L.DomUtil.create("div", 'legend cluster'); | ||
let h3 = L.DomUtil.create("h3", null, div); | ||
h3.innerHTML = "Cohorts by Program End Date"; | ||
let pGroup = L.DomUtil.create("div", "progressGroup", div); | ||
let pBar = L.DomUtil.create("div", "progress", pGroup); | ||
let pLabels = L.DomUtil.create("div", "labels", pGroup); | ||
subgroupsSorted.forEach((group, i) => { | ||
let entry = L.DomUtil.create("div", `entry`, pLabels); | ||
entry.innerHTML = `<div class="label index-${i}">${group.label}</div>` | ||
let bartick = L.DomUtil.create("div", `barTick index-${i}`, pBar) | ||
}) | ||
// div.innerHTML += `<div class="entry"> | ||
// <div class="label reset">Reset</div> | ||
// </div>` | ||
return div; | ||
} | ||
clusterLegend.addTo(map); | ||
function handleClusterLegendClick(e) { | ||
let i = e.target.classList.value.split("-")[1]; | ||
d3.selectAll(`div.barTick`).style("background", | ||
(d,j) => j < i ? "transparent" : "navy" | ||
); | ||
d3.selectAll("div.labels div.entry div.label").style("font-weight", | ||
(d,j) => j < i ? "300" : "500" | ||
) | ||
let chkbxs = document.querySelectorAll("div.leaflet-control-layers-overlays input.leaflet-control-layers-selector") | ||
chkbxs.forEach((c,j) => { | ||
if (j < i && c.checked) { | ||
c.click(); | ||
} else if (j >= i && !c.checked) { | ||
c.click(); | ||
} | ||
}) | ||
// map.flyToBounds(alumni.getBounds()); | ||
} | ||
d3.selectAll("div.legend.cluster div.progressGroup div.progress").on("click", handleClusterLegendClick) | ||
function handleLegendLabelsClick(e) { | ||
let i = e.target.querySelector(".label").classList.value.split("-")[1]; | ||
d3.selectAll(`div.barTick`).style("background", | ||
(d,j) => j < i ? "transparent" : "navy" | ||
); | ||
d3.selectAll("div.labels div.entry div.label").style("font-weight", | ||
(d,j) => j < i ? "300" : "500" | ||
) | ||
let chkbxs = document.querySelectorAll("div.leaflet-control-layers-overlays input.leaflet-control-layers-selector") | ||
chkbxs.forEach((c,j) => { | ||
if (j < i && c.checked) { | ||
c.click(); | ||
} else if (j >= i && !c.checked) { | ||
c.click(); | ||
} | ||
}) | ||
// map.flyToBounds(alumni.getBounds()); | ||
} | ||
d3.selectAll("div.legend.cluster div.progressGroup div.labels").on("click", handleLegendLabelsClick) | ||
}) | ||
</script> | ||
<iframe src="./progress.html" width="95%" height="790px" frameBorder="0" style="border: 0;"></iframe> | ||
<iframe src="./industries.html" width="95%" height="790px" frameBorder="0" style="border: 0;"></iframe> | ||
</body> | ||
</html> | ||
</html> |
Oops, something went wrong.