Public
Edited
Feb 22, 2023
1 fork
Insert cell
Insert cell
Insert cell
viewof year = {
const slider = d3slider
.sliderBottom()
.min(2017)
.max(2021)
.width(300)
.step(1)
.ticks(4)
.tickFormat(d3.format("1"))
.default(2021)
.on("onchange", () => svg.dispatch("input"));

const svg = d3
.create("svg")
.attr("viewBox", [-20, -20, 340, 60])
.attr("width", 340)
.attr("height", 60)
.call(slider);

return Object.defineProperty(svg.node(), "value", {
get: () => slider.value()
});
}
Insert cell
map = {
let container = DOM.element("div", {
style: `width:${width}px;height:${(width * 3) / 4}px`
});

yield container;

let map = L.map(container).setView([42.932616, -78.853392], 13);

let osmLayer = L.tileLayer(
"https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}@2x.png",
{
attribution:
'Data Source: <a href="https://www.airdna.co/vacation-rental-data/app/us/new-york/buffalo/overview">AirDNA</a> | &copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
}
).addTo(map);

let zips = L.geoJson(zipcodes, {
style: style_zips
}).addTo(map);

let nbhds = L.geoJson(neighborhood_maps, {
style: style_nbhds,

onEachFeature: addPopUp
}).addTo(map);

// an invisible layer of zipcodes on the top so you can click on them without being obstructed by the bottom
let invis_zips = L.geoJson(zipcodes, {
onEachFeature: addTooltip,
style: style_invis_zips
}).addTo(map);

function addPopUp(feature, layer) {
layer.bindTooltip(feature.properties.nbhdname, {
permanent: true,
className: "nbhd-label",
opacity: 1,
direction: "center",
offset: [0, 0]
});
}

function addTooltip(feature, layer) {
const f = d3.format(".2f");
let avg =
f(averages.get(year).get(parseInt(feature.properties.ZCTA5CE10))) + "";
let zip = feature.properties.ZCTA5CE10;
let text = `<b>Zipcode:</b> ${zip}<br><b>Average Monthly Listings in ${year}:</b> ${avg}`;
if (!L.Browser.mobile) {
layer.bindTooltip(text, {
className: "zipcode-label",
sticky: true,
direction: "bottom"
});

layer.on("mouseover", function () {
this.setStyle({
color: "white",
weight: 3,
opacity: 1
});
});

layer.on("mouseout", function () {
this.setStyle({
fillOpacity: 0,
opacity: 0
});
});
} else {
layer.bindTooltip(text, {
className: "zipcode-label",
direction: "bottom"
});
}
}

// legend code from https://leafletjs.com/examples/choropleth/

var legend = L.control({ position: "bottomright" });

legend.onAdd = function (map) {
var div = L.DomUtil.create("div", "info legend"),
grades = [0, 20, 40, 60, 80],
labels = [];
if (!L.Browser.mobile) {
console.log("browser is not mobile");
div.innerHTML += `<b>Average Monthly Listings</b><br>`;

for (var i = 0; i < grades.length; i++) {
div.innerHTML +=
'<i style="background:' +
color_scale(grades[i] + 1) +
'"></i> ' +
(grades[i] + 1) +
(grades[i + 1] ? "&ndash;" + grades[i + 1] + "<br>" : "+");
}
} else {
console.log("browser is mobile");
div.innerHTML += `<b>Avg. Monthly<br>Listings</b><br>`;
grades = [0, 50, 100];
for (var i = 0; i < grades.length; i++) {
div.innerHTML +=
'<i style="background:' +
color_scale(grades[i] + 1) +
'"></i> ' +
grades[i] +
(grades[i + 1] ? "&ndash;" + grades[i + 1] + "<br>" : "+");
}
}

return div;
};

legend.addTo(map);
}
Insert cell
mapwidth = map.clientWidth
Insert cell
stylesheet = html`<link href='${resolve('leaflet@1.8.0/dist/leaflet.css')}' rel='stylesheet' />`
Insert cell
L = require('leaflet@1.8.0')
Insert cell
d3slider = require("d3-simple-slider@1")
Insert cell
neighborhood_maps = FileAttachment("neighborhood_maps.json").json()
Insert cell
workbook = FileAttachment("V2_CoreyDockser_BuffaloNews_Buffalo_NiagaraFalls_Ellicottville.xlsx").xlsx()
Insert cell
data = workbook.sheet(0, {headers : true})
Insert cell
data_filtered = data.filter(d => d.city === "Buffalo")
Insert cell
averages_agg = d3.flatRollup(data_filtered, v => d3.mean(v, d => d.total_available_listings), d => d.year, d => d.zipcode)
Insert cell
averages = d3.rollup(data_filtered, v => d3.mean(v, d => d.total_available_listings), d => d.year, d => d.zipcode)
Insert cell
zipcodes = FileAttachment("zipcodes@1.json").json()
Insert cell
style = html`<style type="text/css">

.zipcode-label,
.nbhd-label {
font-family: Georgia !important;
}

.axis text,
.slider text {
font-family: serif;
}

.legend {
# line-height: 18px;
color: #555;
}

.mobile_legend {
}
.legend i {
width: 18px;
height: 18px;
float: left;
margin-right: 8px;
opacity: 0.7;
}

.info {
padding: 6px 8px;
font-family: Georgia;
background: rgba(255,255,255,0.8);
box-shadow: 0 0 15px rgba(0,0,0,0.2);
border-radius: 5px;
}
.info h4 {
margin: 0 0 5px;
color: #777;
}
</style>`
Insert cell
function style_nbhds(feature) {
return {
weight: 1.5,
opacity: 1,
fillOpacity: 0,
color: "rgb(75,75,75)",
};

}
Insert cell
function style_invis_zips(feature) {

return {
fillOpacity: 0,
opacity: 0
};
}
Insert cell
function style_zips(feature) {
return {
weight: 1.5,
opacity: 1,
strokeColor: color_scale(averages.get(year).get(parseInt(feature.properties.ZCTA5CE10))),
fillOpacity: 1,
color: color_scale(averages.get(year).get(parseInt(feature.properties.ZCTA5CE10)))
};
}

Insert cell
color_scale = d3
.scaleThreshold(d3.schemeGreens[6])
.domain([0, 20, 40, 60, 80, 100])
Insert cell
color_scale.domain()
Insert cell
d3.max(averages_agg.filter(d => d[0] === 2021), d => d[2])
Insert cell
averages.get(year).get(parseInt("14203"))
Insert cell
import {view} from '@tomlarkworthy/view'
Insert cell
function swatch(color) {
return html`<div title="${color}" style="
display: inline-block;
width: 10em;
height: 10em;
background: ${color};
"></div>`;
}
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more