Example of choropleth map of us counties using Observable Framework?

Hihi, I’ve been struggling to get a choropleth map of us counties working in Observable Framework. I could benefit from seeing someone do it successfully. Every tile/county is grey even though I believe I am correctly getting/setting different hex codes based on data. To debug, I am emitting the hex code that I think should be coloring the county. It varies by county in the popup so I think I am doing that part right. Just can’t figure out why that same approach doesn’t work for the county tiles…

I’ve been following along examples of people using Observable (Adaptation de Tutorial 7 / leclercprof | Observable) but fear I am not making the right tweaks for Framework. I’ve changed by code too many times to remember what I’ve tried but currently I have something like what’s reproduced below. Any help with be greatly appreciated!

import {us} from "./components/d3-us-map-with-puerto-rico.js";

const data = FileAttachment("data/data.geojson").json();
const countyShapes = topojson.feature(us, us.objects.counties);

// read in county data
const coerceCounty = (d) => ({
    FIPS5: d.FIPS5,
    METRIC1: +d.METRIC1,
    METRIC2: +d.METRIC2,
    ....
  });

const county_data = FileAttachment("data/county_data.csv").csv().then((D) => D.map(coerceCounty));

// was doing this to create a Map object for getting the data I cared about by FIPS
// by abandoned for now
// const countyMap = d3.group(county_data, d => d.FIPS5);

const basemaps = [
  {name: "METRIC1", data: "METRIC1"},
  {name: "METRIC2", data: "METRIC2"}
];
const basemap = view(
  Inputs.select(basemaps, {
    label: "Select basemap",
    format: (b) => b.name,
    value: basemaps.find((b) => b.name === "METRIC1")}));

// TODO see if using a generator here helps?
// const genBasemap = Generators.input(basemap)

function getCountyData(fips) {
  return county_data.find((b) => b.FIPS5 === fips);
};
// setCountyColor(countyMap.get("36001")[0][basemap.data])


function setCountyColor(d) {
  return d > .60 ? '#fc4e08' :
    d > .50 ? '#fd8d1a' :
    d > .20 ? '#feb22a' :
    d > .10 ? '#fed954' :
    '#ffed80';
}

// start making the Leaflet map
// previously tried wrapping all this in a function to call when I wanted it
// but it just renders no matter what.
const geojsonMarkerOptions = {
    radius: 5,
    weight: 1,
    opacity: 1,
    fillOpacity: 0.6
};

const div = display(document.createElement("div"));
div.style = `width:${width}px;height:${width/1.6}px`;


const branchMap = L.map(div)

// add tile layer
const tileLayer = L.tileLayer(cartoLight, {
    attribution: cartoAttr,
    subdomains: 'abcd',
    maxZoom: 20,
    minZoom: 0
}).addTo(branchMap);

// county layer
const countyLayer = L.geoJson(countyShapes, {
  style: (feature) => {
    return {
    fillColor: d => setCountyColor(getCountyData(d.feature.id)[basemap.data]),
    fillOpacity: 0.2,
    // fillColor: '#999999',
    weight: 2,
    opacity: .5,
    color: 'white',
    dashArray: '3'
    }}
})
      .bindPopup(d => d.feature.id + '<br> hex?: ' + setCountyColor(getCountyData(d.feature.id)[basemap.data]))
      .addTo(branchMap)

//geoMarker obj
//passing a pointToLyaer function in a GeoJson options object when creating the GeoJSON layer
const branchesLayer = L.geoJSON(mapData, {
    pointToLayer: (feature, latlng) => {
        return L.circleMarker(latlng, {
            geojsonMarkerOptions,
            fillColor: feature.properties.isMainOffice == false ? "#ff7800" : "#4287f5",
            color: feature.properties.isMainOffice == false ? "#ff7800" : "#4287f5"});
    }
})
    .bindPopup(function (Layer) {
    return Layer.feature.properties.Name +
            '<br> Number: ' + Layer.feature.properties.Number +
            '<br> siteName: ' + Layer.feature.properties.siteName +
            '<br> SiteId: ' + Layer.feature.properties.siteId
        ;
})
    .addTo(branchMap);

branchMap.fitBounds(branchesLayer.getBounds());
// return branchMap

I can’t reproduce your setting with only part of the code and no data, but I think you might want to await the asynchronous operations such as FileAttachment’s csv and json methods (or equivalently put them in separate code blocks so Framework will orchestrate them for you).

If you have further questions about framework please open a discussion on GitHub.

Thanks for the reply. Yeah, the help I can get is only as good as the reprex I provide and mine wasn’t very good. I did have these separated out in separate code blocks in my code but thought it might be easier to read on here if I put it altogether. Really speaks to my low understanding of the underlying mechanics.

At any rate, I spent some more time digging into this and figured out what I need to do. I’ll try to post my solution once I figure out how to create minimal working example minus data I can’t share.

Thanks again.

1 Like