Published
Edited
Jul 20, 2021
Insert cell
Insert cell
map = {
let h = (width<600) ? width + 200: width * 0.75,
z = (width<600) ? 6.5 : 7,
container = DOM.element('div', { style: `width:${width}px;height:${h}px` });
yield container;

let map = L.map(container)
.setView([43.68, -70.26], 13)
// initializes the map, sets zoom & coordinates
let osmLayer = L.tileLayer('https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}@2x.png', {
attribution: '&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
}).addTo(map);
let parcelOverlay = L.geoJson(parcelCollection, {
pointToLayer: function (feature, latlng) {
let val21 = feature.properties.val21.taxableValue,
val20 = feature.properties.val20 ? feature.properties.val20.taxableValue : null,
c = (val20 && (val20 > 0)) ?
( (val21>=val20) ? colorRamp((val21-val20)/val20) : '#542788') : '#fff';
return L.circleMarker(latlng, {
radius: 2,
color: "#000",
weight: 0,
opacity: 0.8,
fillOpacity: 1,
fillColor: c
});
},
onEachFeature: function(feature, layer) {
let val21 = feature.properties.val21.taxableValue,
val20 = feature.properties.val20 ? feature.properties.val20.taxableValue : 'N/A',
pctChange = feature.properties.val20 ? (100*(val21-val20)/val20).toFixed(2) : 'N/A';
layer.bindTooltip(
'<h4>'+feature.properties.address+'</h4>' +
'<p>Total taxable value, 2020: $'+ val20 +
'<br/>Total taxable value, 2021: $' + val21 +
'<br/>Percent change: ' + pctChange + '%</p>'
)}
}).addTo(map);
map.on('zoomend', function() {
var currentZoom = map.getZoom();
let z = (currentZoom > 14) ? 5 : 2;
let s = (currentZoom > 15) ? 1 : 0;
parcelOverlay.eachLayer(function (marker) { marker.setRadius(z); marker.setStyle({weight: s}); });
});
let legend = L.control({position: 'bottomleft'})
legend.onAdd = function (map) {var div = L.DomUtil.create('div', 'legend'); return l};
legend.addTo(map);

}
Insert cell
md`# Taxable value per acre, 2021

This map illustrates the ratio of each parcel's taxable value to its acreage, according to the assessor's database. Smaller parcels on the Portland peninsula generally have higher taxable values and lower land areas, generating a higher value per acre.

Hover over a point to see assessment details from the assessor's database.`
Insert cell
valuePerAcreMap = {
let h = (width<600) ? width + 200: width * 0.75,
z = (width<600) ? 6.5 : 7,
container = DOM.element('div', { style: `width:${width}px;height:${h}px` });
yield container;

let valuePerAcreMap = L.map(container)
.setView([43.68, -70.26], 13)
// initializes the map, sets zoom & coordinates
let osmLayer = L.tileLayer('https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}@2x.png', {
attribution: '&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
}).addTo(valuePerAcreMap);
let parcelOverlay = L.geoJson(parcelCollection, {
pointToLayer: function (feature, latlng) {
let val21 = feature.properties.val21.taxableValue,
a = feature.properties.acreage,
c = (a > 0) ?
colorRamp2(val21/a) : '#fff';
return L.circleMarker(latlng, {
radius: 1.5,
color: "#000",
weight: 0,
opacity: 0.8,
fillOpacity: 1,
fillColor: c
});
},
onEachFeature: function(feature, layer) {
let val21 = feature.properties.val21.taxableValue,
a = feature.properties.acreage,
vpa = (a>0) ? (val21/a).toFixed(2) : 'N/A';
layer.bindTooltip(
'<h4>'+feature.properties.address+'</h4>' +
'<p>Total taxable value, 2021: $'+ internationalNumberFormat.format(val21) +
'<br/>Acreage: ' + a + ' acres' +
'<br/>Per acre: $' + internationalNumberFormat.format(vpa) + '</p>'
)}
}).addTo(valuePerAcreMap);
valuePerAcreMap.on('zoomend', function() {
var currentZoom = map.getZoom();
let z = (currentZoom > 14) ? 5 : 2;
let s = (currentZoom > 15) ? 1 : 0;
parcelOverlay.eachLayer(function (marker) { marker.setRadius(z); marker.setStyle({weight: s}); });
});
let legend = L.control({position: 'bottomleft'})
legend.onAdd = function (map) {var div = L.DomUtil.create('div', 'legend'); return l2};
legend.addTo(valuePerAcreMap);

}
Insert cell
L = require('leaflet@1.2.0')
Insert cell
html`<link href='${resolve('leaflet@1.2.0/dist/leaflet.css')}' rel='stylesheet' />`
Insert cell
topojson = require("topojson-client@3")

Insert cell
parcelCollection = {
let a = new Object;
a.type = "FeatureCollection";
a.features = parcelGeo;
yield a;
}
Insert cell
parcelGeo = parcelFile.map( d => {
let o = new Object;
o.type = "Feature";
o.properties = d;
o.geometry = {"type":"Point", "coordinates": [+d.lng,+d.lat]};
return o;
});
Insert cell
parcelFile = FileAttachment("assessmentdata.json").json()
Insert cell
import {legend} from "@d3/color-legend"
Insert cell
colorRamp = d3.scaleLinear()
.domain([0,0.52,3])
.range([ "#2166ac", "#ffffbf", "#b2182b"])
Insert cell
colorRamp2 = d3.scaleLinear()
.domain([100000,10000000,40000000])
.range([ "#b3cde3", "#756bb1",'#b30000'])
Insert cell

internationalNumberFormat = new Intl.NumberFormat('en-US')
Insert cell
l2 = legend({
color: colorRamp2,
title: "Taxable value per acre, 2021",
tickValues : [1000000,10000000,40000000],
tickFormat: d3.format("$,.1s")
})
Insert cell
l = legend({
color: colorRamp,
title: "% in total assessed value, 2020 to 2021",
tickFormat: d => (d*100)+"%"
})
Insert cell
d3 = require('d3@5.0')

Insert cell

One platform to build and deploy the best data apps

Experiment and prototype by building visualizations in live JavaScript notebooks. Collaborate with your team and decide which concepts to build out.
Use Observable Framework to build data apps locally. Use data loaders to build in any language or library, including Python, SQL, and R.
Seamlessly deploy to Observable. Test before you ship, use automatic deploy-on-commit, and ensure your projects are always up-to-date.
Learn more