Pharmacy Deserts Basemaps & Data

Walgreens and Rite Aid are closing many stores, leaving pharmacy deserts in their wake.

Earlier this year, I tried to help any data journalists out there keep track of the closings. The Walgreens project and Rite Aid project have code for grabbing the current list of stores with their location data, and an data from initial run of the code.

These should provide a solid base for folks to work from and help us understand where the greatest impacts will be felt.

Data Tools Used

  • R
  • Rust
  • Go

Mapping Tools Used

  • Observable Plot

Today’s Entry Notes

I’m using Quarto, and the latest Observable Plot enhancements have not made it to there, yet, so my plan to use the Pointer transform to show popups on hover for each state was not possible.

While Observable Plot most certainly supports faceting, it was easier to just make the function, below, vs. try to fiddle with the positioning.

By using the className option for Plot.plot(), we get the ability to style the plot a bit more using CSS (like I did for these), or target the selector for futher manipulation with javascript.

It also turned out to be quicker to return a <div> wth the logo and plot in it than to fiddle with Plot.image(). I ended up having to convert the Walgreens official logo to an SVG from EPS and hand-editing the Rite Aid logo to remove the width and height attributes so it woul scale with the container.

This was also good practice in creating a parameterized function wrapper for Plot.

Code
function storePlot(opts) {
  const plt = Plot.plot({
  className: opts.class,
  caption: opts.caption,
    width: width,
    height: width * (3 / 4),
    style: {
        backgroundColor: "transparent",
    },
    projection: "albers-usa",
    marks: [
        Plot.geo(nation, {
            strokeWidth: 1.5,
            stroke: "#fff",
            strokeOpacity: 1 / 10,
            fill: "#111",
        }),
        Plot.geo(statemesh, {
            strokeWidth: 0.25,
            strokeOpacity: 1 / 5,
        }),
        Plot.geo(nation, {
            stroke: "#999",
        }),
        Plot.dot(
            stores.filter((d) => d.chain == opts.chain),
            {
                x: "longitude",
                y: "latitude",
                fill: opts.color,
                fillOpacity: 3 / 4,
                stroke: "#00000000",
                r: 0.5,
            }
        ),
    ],
});

  

  return html`<div style="position: relative;">
        ${plt}
        <img
            style="position: absolute; top: ${opts.top}; right: ${opts.right};"
            width="${opts.width}" src="${opts.logo}"
        />
    </div> `;
}

// This is a geojson file we'll see quite a bit of this month
us = FileAttachment("static/data/us-counties-10m.json").json();

// taking various subsets to plot as different layers
statemesh = topojson.mesh(us, us.objects.states);
states = topojson.feature(us, us.objects.states).features;
nation = topojson.feature(us, us.objects.nation);

// our store data
stores = FileAttachment("./static/data/2023-11-01.json").json();

walgreensRed = "#E31836";
riteAidGreen = "#99c45a";

walgrens = storePlot({
    chain: "Walgreens",
    class: "walgreens",
    caption: "All U.S. Walgreens stores as of 2023-11-01",
    color: walgreensRed,
    logo: "static/walgreens-logo.svg",
    width: "50%",
    top: "0%",
    right: "10%",
});

riteAid = storePlot({
    chain: "Rite Aid",
    class: "rite-aid",
    caption: "All U.S. Rite Aid stores as of 2023-11-01",
    color: riteAidGreen,
    logo: "static/rite-aid-logo.svg",
    width: "25%",
    top: "4%",
    right: "10%",
});