The Bay Shore Power House

In Maryland’s North Point State Park, along the Black Marsh Trail, there is an abandoned building.  This is the story of that building.

In short: It was a power generating station operated in the early part of the twentieth century to provide electricity to a nearby trolley line and amusement park.

The not-so-short explanation follows.

§ Background

Some information is needed to set the stage before we get to the power house itself.

# North Point and Sparrows Point

The land southeast of Dundalk forms a peninsula that frames the east side of the Patapsco River.  One of the earliest names for the tip of that peninsula was North Point, named by and after Captain Robert North.  Captain North was one of the original land owners in Baltimore when the city was formed in the early eighteenth century.  North Point was used by the US military to build defensive structures in the late nineteenth century; the installation was named Fort Howard and was intended to protect the Baltimore Inner Harbor and, to a degree, the upper Chesapeake Bay from naval incursion.  (The land is now divided between a county park and a fenced off, abandoned former Veterans Administration hospital.)

Just upstream from North Point on the Patapsco River is Sparrows Point, land named by and after Thomas Sparrow, Jr.  He was granted the land in the mid seventeenth century by the Second Baron Baltimore, then the English proprietor of the colony of Maryland.  In the late nineteenth century, the land was developed into a steel plant by the Pennsylvania Steel Company.  It was purchased by Bethlehem Steel in 1916.  (The steel plant was operated by Bethlehem Steel until the latter’s bankruptcy in 2001. It was then sold among several different companies through the following decade until it was decommissioned in 2012 after years of declining utilization.  Sparrows Point has since been redeveloped into a logistics hub, connecting sea, rail, and truck transportation of a wide variety of goods.)

# Baltimore’s Trolleys

There were a number of companies operating electric trolleys in Baltimore in the late nineteenth century, but all of them merged together in 1899 to form the United Railways and Electric Company.  That consolidated company, often referred to as UR&E, both operated a number of trolley lines and several power plants.  The power plants gave the company direct control over the bulk of the power its trolleys used.  Power was also sold to other customers.

UR&E acquired a few power plants through corporate consolidation, but its primary source of electricity came from the Pratt Street Power Plant.  (The latter facility ceased generating power in 1973, but has in the years since hosted a number of significant tenants, including an indoor Six Flags, an ESPN Zone, a Barnes & Noble, and a Hard Rock Cafe.)

In 1903, UR&E, through a subsidiary, built a rail line to Sparrows Point and the steel plant that operated there.  That line was called the Sparrows Point Line and given the number 24.  (It would later be renumbered as 26.)

# Bay Shore Park

In 1906, UR&E built an amusement park near North Point on the shore of the Chesapeake Bay.  They named the park the Bay Shore Amusement Park, often shortened to just Bay Shore Park.  These sorts of parks were not uncommon for trolley companies of the day; such parks provided significant income for the companies from both the money spent at the parks and the fares paid to travel to the parks via trolley.  These parks were often referred to as trolley parks; one of the better-known ones is probably Coney Island in New York City.  At the time, there were several other such parks operating in the Baltimore area already.

Bay Shore Park was a popular destination for Baltimore residents for nearly half a decade.  It had a high class restaurant and a number of entertainments and rides, several of which took advantage of the proximity of the bay.  One of the more unique and popular attractions it had when it opened was the Sea Swing, also called the “People Dipper”.  It was a set of swings that were spun around in a circle, much like modern amusement parks’ swing rides.  The Sea Swing, however, was out in the water and would swing people at least partially into the bay.  Other attractions included water slides and toboggans that terminated in the bay.  The park would rent swimming suits to people who hadn’t brought their own.  There were, of course, attractions that didn’t require getting wet, like a roller coaster, a movie theater, and a bowling alley.

Access to the park was provided by an extension of the Sparrows Point Line.  The extension operated as a loop:  trolley cars would leave Sparrows Point, travel out to Bay Shore Park, head along the shore to Fort Howard, then loop back to Sparrows Point.  (Or the trolley cars might have gone in the other direction, visiting Fort Howard before Bay Shore Park.  I’ve found conflicting descriptions of the route.)

It’s worth pointing out that Bay Shore Park, like many places at the time, was racially restricted.  Only white people were allowed in.  I’ve been unable to determine if it was ever desegregated, or if black people were still being denied access when it closed in 1946.  The latter seems likely, based on the number of places in Baltimore that remained segregated into the 1950s.

§ Bay Shore Power House

The power house in 1924, according to the Maryland Rail Heritage Library

The power house in 1924, according to the Maryland Rail Heritage Library

The above finally brings us to the power house.

# The United Railways and Electric Company’s Power Needs

After the initial construction on the Bay Shore extension to the Sparrows Creek Line and the Bay Shore Park, UR&E decided it needed to build a new power source near the park to accommodate the additional load being added.  They determined that building a new facility would be cheaper than adding both more capacity to the Pratt Street Power Plant and appropriate infrastructure to carry the electricity down to where it would be needed.

The new power house was located roughly where the trolley line reached the shore of the Chesapeake Bay, about a mile northeast of the amusement park.  It appears to have been constructed in 1907.  The initial output of the power house was about 1,375 kW.  For comparison, the Pratt Street Power Plant was generating around 30,000 kW of power, of which about 5,000 was dedicated to the trolley system.

The power generating equipment consisted of a number of steam-powered turbines that generated direct current, as used by the trolley system.  The steam was generated by a number of coal-fired boilers.  Coal was delivered to the plant overnight by a dedicated car that carried it from the coal supplies at the Pratt Street Power Plant.

The 1910 UR&E annual report says, “The capacity of the Bay Shore Power Station has been increased by the installation of one of the units removed from a disused plant.”

A 1914 Sanborn Insurance Map showed four boilers, eight turbines, and 4,325 kW of generating capacity.

The 1915 UR&E annual report indicated that the power house continued to operate as expected.

As a side note, the ownership of various UR&E assets gets a bit convoluted at times.  The company didn’t own either the trolley track or the power house directly itself.  Instead, they were owned by companies that were, in turn, owned by UR&E.  The Sparrows Line track was owned by UR&E subsidiary the Baltimore, Sparrows Point, and Chesapeake Railway Company.  The power house was owned by UR&E subsidiary the Maryland Electric Railways Company.

# UR&E Stops Generating Its Own Power

In the first decade of the twentieth century, UR&E found that running its own power plants was preferable to purchasing power from other companies.  Their own power was more consistent and reliable.  Toward the end of the decade, they began to try purchasing power from a newly-constructed hydroelectric plant on the Susquehanna River.  Cost seems to have been the driving factor here; the hydroelectric plant could generate electricity far more cheaply than UR&E could do itself.

For a few years, the McCall Ferry Power Company, owners of the hydroelectric plant, had difficulty meeting the terms of the contract to supply the electricity.  By the early 1910s, however, the company had renamed itself Pennsylvania Water & Power, and its electricity was reliable enough that UR&E began to rely more and more on PW&P power.

By 1912, the majority of UR&E’s electricity came from Pennsylvania Water & Power.  The Pratt Street Power Station continued operating just to supplement the power from PW&W.  The Bay Shore Power House also continued operation, but only during the summer months, when traffic to Bay Shore Park needed the extra power.

A 1915 UR&E annual report mentions that the Bay Shore Power House “was operated in its regular service during the year.”

At some point, maintaining the Bay Shore Power House’s power generation became too much of a burden for UR&E.  They stopped running the generators at Bay Shore and instead used the facility solely as a substation.  Its principal purpose at this point was to transform incoming AC power to the DC power needed by the trolley line.

In 1921, UR&E stopped generating any power itself.  It sold the Pratt Street Power Plant to the Consolidated Gas, Electric, Light and Power Company of Baltimore.  The Bay Shore Power House was not sold at this time, which is circumstantial evidence that it was not in use for generating power by 1921.  (The Consolidated Gas, Electric, Light and Power Company of Baltimore eventually became Baltimore Gas and Electric, which is now a part of Constellation Energy.  After purchasing the Pratt Street Power Plant, the new owner largely discontinued use of the aging facility for electricity production.  The plant would be briefly revived for generating electricity during World War II before being fully retired in 1973.)

I’ve been unable to pinpoint when, exactly, power generation was discontinued at the Bay Shore Power House, but it seems to have taken place sometime between 1915 and 1921.

# Service After the United Railways and Electric Company

In 1933, UR&E entered bankruptcy proceedings.  In the same year, a hurricane damaged part of the Sparrows Point Line near North Point.  It effectively split the loop at the end of the line into two branches.  Rather than repair the damaged track, UR&E simply began running separate service to the two branches.  Trolleys would alternate between traveling to Fort Howard and to Bay Shore Park.

UR&E exited its bankruptcy proceedings in 1935, when all of its assets were transferred to the newly-created Baltimore Transit Company.  The former Bay Shore Power House, now just a substation, was transferred along with the rest.

Sometime around 1940, the Baltimore Transit Company began leasing Bay Shore Park to a local contractor, George Mahoney.  This only affected operations of the park, not the trolley, so ownership and management of the former Bay Shore Power House remained with Baltimore Transit.  George Mahoney ended up buying the park from Baltimore Transit in 1944.  He then sold it again to a new owner, O.L. Bonifay, in 1946.

In 1946, after the end of the summer season, the Sparrows Point trolley line ceased service on the Bay Shore Park branch.  After that point, trolleys only went to Fort Howard.  The former Bay Shore Power House was presumably shut down fully at this point, assuming it hadn’t been decommissioned earlier.

In 1947, Bethlehem Steel purchased a large swath of land around and including Bay Shore Park.  They bought the park itself from O.L. Bonifay, the disused trolley right of way and the Bay Shore Power House property from Baltimore Transit, and various other pieces of land from their respective owners.  There seem to be conflicting opinions about whether Bethlehem Steel intended to use the land to expand its own operations or if it merely wanted to deny its competitors the opportunity to move into the area.  Either way, the company moved the Bay Shore Park attractions to nearby Pleasure Island and then ceased to do anything further with the purchased land.

(Pleasure Island is an island located a few miles northeast of the former Bay Shore Park location.  Bethlehem Steel opened a new amusement park there with the old Bay Shore Park equipment.  The new amusement park was named New Bay Shore Park.  New Bay Shore Park closed in the 1960s after a storm washed out the only bridge connecting it to the mainland.  Pleasure Island is now part of Hart-Miller Island State Park, despite not being connected to Hart-Miller Island.)

In 1987, Bethlehem Steel sold the land around the former Bay Shore Park to the Maryland Department of Natural Resources.  Maryland DNR used the land to create North Point State Park.

# The Rest of the Sparrows Point Line

The fate of the rest of the Sparrows Point Line doesn’t really have any bearing on the former Bay Shore Power House, but since I’ve touched on it a bit, I’ll bring that thread to a conclusion.

In 1948, the Baltimore Transit Company was purchased by National City Lines, a public transit company funded by several car manufacturing companies, principally General Motors.  National City Lines purchased trolley companies all over the country with the apparent intent to replace trolleys—which General Motors did not manufacture—with buses—which GM did manufacture.  Baltimore was no exception to this trend.  The section of the Sparrows Point Line between Sparrows Point and Fort Howard was converted to bus service in 1952.  The Sparrows Point Line was fully replaced by buses in 1958.

The Baltimore Transit Company finished getting rid of its trolleys in the 1960s.  The last of its trolleys made its final trip in 1963.  When, in 1970, Maryland created the Baltimore Metropolitan Transit Authority—now the Maryland Transit Administration—it took over all of the Baltimore Transit Company’s assets and bus routes.

§ Present State

The interior of the power house in July 2025

The interior of the power house in July 2025

This building now lies within North Point State Park.  To reach it, you can enter the park, park in lot A (the first parking lot after entering the park), and walk the Black Marsh Trail for approximately a mile.  This section of the trail follows the path of the old Sparrows Point Line, so the old power house is immediately adjacent to the trail.

The building is in surprisingly good condition, given its age, but any such abandoned structure carries risk of structural failures leading to injury.  In other words, enter at your own risk.

As of 2025, the concrete walls and support structures are still standing, but the interior is completely empty.  On one side of the building, you can see patterns in the concrete floor where the boilers were located, and there are holes in the ceiling through which the boilers’ exhaust stacks passed.  The back wall, behind where the boilers were, is open to the outside, with concrete columns supporting the roof.  I’m unsure whether it was open during operation, or if there was some non-concrete wall that no longer survives.

The walls and columns in the building are covered in graffiti.

§ When Was the Power House Built?

In my research, I came across a number of different (but not that different) claims about when the power house was built.  The 1914 Sanborn insurance map says it was built in 1905.  Elmer Hall’s The No. 26 Sparrows Point Line says it was built in 1906, citing the UR&E’s ninth annual report, which covered the company’s activities in 1907.  Hall’s claim seems to have flowed into a number of other publications; the most common date I came across for the building’s construction was 1906.

The UR&E’s eighth annual report, which covered the company’s activities in 1906 and was published in April 1907, says:

Recognizing that … it would be necessary to have an independent plant for operation of this line … the Maryland Electric Railways Company has purchased a suitable site adjoining Bay Shore Park and will erect thereon a power and lighting plant to be leased to your Company.

The UR&E’s ninth annual report, which covered the company’s activities in 1907 and was published in April 1908, says:

It was deemed advisable to locate a power plant in close proximity to Bay Shore Park, a site 500 feet back from the shore having been selected for this purpose, and a reinforced concrete building constructed thereon.

The deed establishing the purchase of the land for the power house (Baltimore County land records, Liber WPC 313, folio 10) is dated March 28, 1907, but that was a transfer from the North Point Land Company to Maryland Electric Railways.  It appears that North Point Land Company was a shell company established to purchase and consolidate land for Bay Shore Park in advance of building anything.  The section of land the power house is on was purchased by the North Point Land Company in 1905 (Baltimore County land records, Liber WPC 283, folio 162).  It’s conceivable that the 1906 annual report was referring to the North Point Land Company’s acquisition when it said a site had been purchased.

Regardless, the company uses the future tense to talk about the power house in its 1906 report, and the past tense to talk about it in the 1907 report.  That seems like solid evidence that the building was actually constructed in 1907.

§ Afterword

This was rather a lot of work to compile information on a building that was in use for just a handful of decades and has been abandoned for more than three quarters of a century.

Along the way, though, I found a fascinating number of intersections with the history of Baltimore, its trolley system, a couple old amusement parks, and the predecessors to BGE (now Constellation Energy), the MTA, and Sparrows Point.  I hope, if you made it this far, that at least some of that was interesting to you, too.

§ Bibliography

Falter, Dennis.  “The Baltimore Coal Car.”  The Live Wire (2004).

Hall, Elmer J.  The No. 26 Sparrows Point Line.  Gazette Printers, 2017.

Harwood, Herbert H., Jr.  Baltimore and its Streetcars.  Quadrant Press, 1984.

Historic American Engineering Record.  Pratt Street Power Plant, HAER MD-101.  National Park Service.

In Re United Railways & Electric Co. of Baltimore’s Reorganization, 11 F. Supp. 717 (D. Md. 1935).

King, Thompson.  Consolidated of Baltimore: 1816–1950.  Consolidated Gas Electric Light and Power Company of Baltimore, 1950.

Sanborn Map Company.  Insurance Maps of Baltimore, Maryland, 1914, Vol. 5, p. 518.  Sanborn Map Company, 1914.

United Railways and Electric Company of Baltimore.  Collected annual reports 7–17.  Baltimore: The Sun Job Printing Office, 1906–1916.

United Railways and Electric Company of Baltimore.  Eighteenth Annual Report.  Baltimore: The Sun Job Printing Office, 1917.

United Railways and Electric Company of Baltimore.  Twentieth Annual Report.  Baltimore: The Sun Job Printing Office, 1919.

And okay, this wasn’t a source for this article, but I absolutely have to talk about Bay Shore Park: The Death and Life of an Amusement Park by Victoria Crenson, published by W.H. Freeman in 1995.  It’s an illustrated children’s book about nature reclaiming Bay Shore Park after all the rides and amusements were moved away.


Color Schemes for Data Visualization

I use colors a lot in preparing data visualizations.  This post mostly serves to collect together the various resources I use when choosing color schemes for particular visualizations or other tasks.

§ General Considerations

When choosing a color scheme, there are a number of different considerations it’s useful to keep in mind.  Some of the things I consider are given below; this is mostly to provide context for why I like the color schemes I like.

# Type of Color Schemes

Color schemes are generally designed around particular use cases, which are often largely determined by the nature of the data being visualized.

Categorical color schemes are used when the data is represented as several distinct things and you just need to be able to tell those things apart.  One common example is when you have a line graph with several lines representing different trends over time.  You need to be able to tell which line on the graph corresponds to a particular category, so each color should be distinct from all the others.  Another example would be a map where different areas have different characteristics, such as election maps where you want to represent the party of the candidate that won in each district.

Linear color schemes are used when you want to represent data that varies along some numeric continuum.  For example, you might want to color points on a map according to their temperature.  Cooler temperatures would get one color, warmer temperatures would get another, and the temperatures in between would get colors in between.

Diverging color schemes are similar to linear schemes, but they’re used when there’s some central numerical value for the data and you want to represent deviations from that central value.  For example, if you had a map showing an election between two candidates, you could use a diverging color scheme to show the strength of each district’s support for its winning candidate.  A district that went strongly for one candidate would get one color.  A district that went strongly for the other candidate would get a different color.  A district that was evenly split would get the central, neutral color.

Circular color schemes are used for data that either cycles around or repeats itself.  For example, wind direction can be considered circular; the wind might come from a direction anywhere from 0° to 359°, but 0° and 359° are very close to each other and should have colors that are similarly very close to each other.

Linear, diverging, and circular schemes can be either continuous or segmented.  When used continuously, every separate data point gets its own, specific color.  A value in a linear color scheme 26.5% of the way between the two endpoints will get a color 26.5% of the way between the two endpoints’ colors.  If the color scheme is segmented, the data is divided into separate groups (often called bins or buckets) and each group gets its own color along the gradient.  For example, if you used a 10-bin division of the data space, the 26.5% value would get the same color as every value between 20% and 30% (and that color would probably be something around 25% of the way between the two endpoints’ colors).

# Things to Care About in Color Scheme Design

There are several things that are important to consider when choosing a good color scheme.  Different use cases will have different criteria; characteristics that are important for a categorical map will be different from those important to an elevation model.

It should be easy to identify distinct states in the color scheme.  For a categorical scheme, every color should be distinct from all the others.  For linear schemes, at least the endpoints should be distinct from each other.  For a diverging scheme, the two endpoints and the midpoint should all be distinct from each other.

In some cases, there is semantic meaning to particular colors as they relate to the data being displayed.  You should choose colors with meaning congruent to the data or, at least, avoid colors that would be counterintuitive.  For example, you probably shouldn’t use a scheme that uses red for safe values and green for dangerous values.

You should take into account the potential limitations of people viewing your visualization.  For example, many people have red-green color blindness; a color scheme that goes only from red to green hues will likely not be very useful to such people.

You should also take into account limitations of your medium.  If your visualization will be printed on black and white printers, for instance, you will need to make sure the colors are distinct even if there’s no hue, just lightness and darkness.

Color schemes should be perceptually uniform.  The human eye does not perceive colors uniformly.  For instance, green light often appears brighter to us than blue light of the same physical brightness.  In computer terms, the RGB color #00FF00 feels much brighter then #0000FF.  Many color schemes are carefully designed so that as you move along the scheme’s color gradient, it feels like the brightness changes in proportion to the numeric difference.  If a color scheme has not been designed with human perception in mind, the same numerical difference can feel different depending on which part of the gradient they span.

Non-categorical color schemes should feel like there’s a natural order to them.  If you take two colors from anywhere on the gradient, the one that feels like it’s “more” than the other should always point in the same direction.

§ Resources

With the considerations for color choice out of the way, we can now move on to the various resources I use when choosing colors to use for a project.

# ColorCET

URL: https://colorcet.com/
Author: Peter Kovesi.

ColorCET consists of a large number of perceptually uniform gradients for a variety of different scenarios.  There are linear, diverging, and circular gradients with several different hue transitions.  There are several schemes designed for more specific purposes, like ones for geographic elevation mapping, or the set of red, green, and blue gradients intended for putting three separate data channels into a single RGB image.

Although the schemes are all designed to be perceptually uniform, only a small subset are designed around broadly-applicable utility for color blindness.

I tend to default to ColorCET because the variety of schemes means there’s usually something available that has a combination of hues particularly suited to the use case I have in mind.

# Scientific Colour Maps

URL: https://www.fabiocrameri.ch/colourmaps/
Author: Fabio Crameri

Fabio Crameri’s scientific color maps are all developed with color blindness in mind and should be accessible across a wide array of vision differences.  There are linear, diverging, and circular gradients, as well as adaptations of the color schemes to categorical use.  There are also some “double diverging” color schemes; I can’t comment too much on them, since I’ve never found a need for that sort of scheme myself.

The color schemes are all use fairly muted colors.  The intent is that these would be used for visualizations in published papers or as part of some other, larger work.  The colors are designed to blend in with surrounding text.  Crameri argues that many other color schemes draw too much attention to the visualizations that use them because their colors are more vivid than the text they’re supporting.

There are several different color schemes here.  When I use something from here, I mostly just use “batlow” or one of its variants; none of the other schemes feel distinct enough to warrant me choosing them over that default.

# cmocean

URL: https://matplotlib.org/cmocean/
Author: Kristen Thyng

The cmocean color schemes are, in many cases, based on Matplotlib colormaps, but they’ve been adjusted to be perceptually uniform.  The various colormaps are chosen and named according to common color schemes for particular oceanographic visualizations.  For instance, the “thermal” map has colors commonly used in oceanic temperature visualizations.

# Paul Tol’s Colour Schemes

URL: https://sronpersonalpages.nl/~pault/
Author: Paul Tol

Paul Tol works on visualization of geodata from satellite observations.  He’s developed a number of different color schemes for use in websites and papers.  These color schemes are perceptually uniform and color blind safe.

A number of the schemes include a separate color specifically intended to represent points with no data available.

I rather like these schemes.  There aren’t too many to choose from, which avoids some of the decision paralysis some other collections engender.  The colors are also relatively simple; the schemes feel more natural to me than, say, Fabio Crameri’s colors.  I’ve been making more use of these colors over time, especially for categorical data.

# ColorBrewer

URL: https://colorbrewer2.org/
Author: Cynthia Brewer

ColorBrewer is a classic of the genre, if you will.  It was originally developed in the very early 2000s to provide a number of color schemes intended for coloring map areas.

The color design assumes quantized data buckets.  None of the color schemes are continuous; their maximum number of distinct colors ranges from nine to twelve (and, frankly, the twelve-color schemes are pushing the boundaries of being visually distinct).  The linear color maps are perceptually uniform.  Not all of the schemes are color blind or black-and-white printing safe, but the website makes it pretty easy to select the ones that are.

I used to use this site all the time.  Now I’m more likely to use ColorCET or Paul Tol’s colors than ColorBrewer, but I do occasionally come back here, particularly for map-related visualizations.

# Viridis

URL: https://cran.r-project.org/web/packages/viridis/vignettes/intro-to-viridis.html
Authors: Bob Rudis, Noam Ross, and Simon Garnier

Viridis is both a collection of color maps and the name of the principal map in the collection.  All of the color schemes in the collection are perceptually uniform and color blind safe.

The viridis colormap is the default in Matplotlib.  (It was made the default in the Matplotlib 2.0 release).

The cvidis colormap is designed for functionality across a wide range of vision color deficiencies.

The turbo colormap is a rainbow colormap aimed at being a perceptually-uniform alternative to Jet.  It covers all the hues of a rainbow, with greater lightness in the center and lower lightness at each of the two ends.  It’s intended for uses where you don’t necessarily care about linear color ordering so much as identifying parts of the image with the same values as each other.  It’s not strictly color blind safe, but it’s better than some alternative rainbow schemes.

I have on occasion used the turbo colormap for visualizing depth maps.

# 20 Simple, Distinct Colors

URL: https://sashamaps.net/docs/resources/20-colors/
Author: Sasha Trubetskoy

This is a set of twenty colors designed to be visually distinct from each other while covering most of the useful hues of the rainbow.  There are options for limiting the selection to color blind safe palettes, though you end up with fewer usable colors.

These are only really useful for categorical data.  They’re not perceptually uniform.  The author doesn’t even use them for their original purpose, having found a better approach that used fewer colors overall.

In general, I tend to default to Paul Tol’s color schemes for categorical data before I reach for these.  I do, however, like Sasha’s color names.  When I need to pick colors that correspond to a number of specific names, I find this is a useful resource.

# Reasonable Colors

URL: https://www.reasonable.work/colors/
Author: Matthew Howell

Reasonable Colors comprises a set of colors designed principally for websites, through they’re applicable more broadly, too.  It consists of a set of twenty-four hues evenly distributed across a perceptual color space, plus gray.  Each color has six shades.  The shades are perceptually matched, so the, say, third shades of two different colors will have the same lightness (or darkness) levels.

I’ve used these colors for website design, including for choosing some of the colors for this website.  I also sometimes use these when matching named colors, though my usual resource for those is Sasha Trubetskoy’s color collection, mentioned above.

# Color Map Advice for Scientific Visualization

URL: https://www.kennethmoreland.com/color-advice/
Author: Kenneth Moreland

This page discusses a number of different colormaps, but the main reason I want to link to it is its Fast color scheme.  The Fast scheme provides a blue-white-red diverging colormap where the white in the middle covers a fairly narrow band of data.  It’s very useful for visually locating the transition bands between the two halves of the colormap.

The Fast map in particular is designed to be useful when applied to a 3D surface.  I have yet to use any colormap in that manner, but I appreciate some of the other qualities of the scheme.

# Tree Colors: Color Schemes for Tree-Structured Data

URL: https://doi.org/10.1109/TVCG.2014.2346277
Authors: Martijn Tennekes and Edwin de Jonge

This isn’t a color scheme, but a coloring algorithm designed to make coherent but visually distinctive categorical palettes for hierarchical tree-structured data.  I have used this algorithm for coloring treemaps.

Broadly speaking, the algorithm assigns different hues to different branches of the tree, and chooses the hues so adjacent branches will have distinct colors.  The color saturation increases from the root out to the leaf nodes, so it’s an indicator of the depth of a given node.


What to Know About Trans People

If there’s one thing I want everyone to understand about trans people, it’s this:

Trans people live—or strive to live—their lives as themselves.

Trans women live as women. Trans men live as men. Nonbinary people cover a broad spectrum, but they all just want to be able to be themselves in every facet of their lives.

There is no “pretending” here. There is no “lying”. Sometimes there is experimenting or trying things out to help figure out who they really are, but that’s still experimentation, not falsehood.

There’s this narrative from a lot of places, not least of which being our current federal administration, that trans people are acting out some false identity. That narrative is the exact opposite of reality.

Trans people are trying to be themselves in the face of a world that has told them, since birth, who it thinks they should be. In many cases, trans people’s understanding of themselves is very hard-won. It can take years or even decades to reconcile the internal feeling of who you are with the external pressures to be someone else.

It is a testament to the truth of trans identities that so many people have stood up to that world and said, “I am who I know myself to be, not who you say I am, and in spite of you saying it to me throughout my entire life.” It is a shadow over our society that many other trans people have been driven to suppress that identity—to lie about themselves in order to fit into the world—or to end their lives rather than have to choose between the lie of conformity and a truth that would leave them ostracized and isolated.

Trans people just want to live the truth of their own identities.


safe-cp: A safer file copy script

A while back I wrote a shell script to do a “safe” file copy on a Linux system.  I use it somewhat frequently for managing servers.  It basically copies a file in a completely atomic way.  Programs accessing the file always see either the complete original file or the complete new file; never anything else.

Because it’s a little involved, I though it would be nice to share it publicly, so here it is.

The script is in safe-cp.sh.  You copy a file with safe-cp.sh source-file destination-file.  More details are available by running safe-cp.sh --help.

The program ensures three things:

  1. If the destination file existed before running the program, a file always exists with that name.  There is never a time when the old file has been removed but the new one has not yet been created.

  2. The destination file is always complete and whole.  Either the entirety of the old version of the file is available or the entirety of the new version of the file is available.  There is never a time when the destination file is either empty or contains an incomplete copy of either the old or new data.

  3. The contents of the original file always exist in the filesystem.  After the copy, the old data exists—with the same inode—in a copy of the original file with a different name.  This ensures that if a program has an open filehandle to the file, its view of the data is unchanged.  The program won’t see the new data unless it closes and reopens the file, but it will never see inconsistent data.

You’d think the guarantees in the last point would be a given because of how Unix handles unlinking files with open filehandles, but I’ve seen NFS mounts that didn’t behave correctly in this regard.

Fundamentally, the script works by making sure a copy of the new file exists in the same filesystem as the destination, hard-linking the old file to a new filename, then renaming the copy of the new file to the destination name.  That final rename is atomic, while the hardlinking ensures the original file and inode are preserved.

You can read through the script for the precise details, of course.


Cairo and Pango in Python

Cairo is a pretty useful vector-based graphics library.  I usually use it from Python.  But Cairo’s built-in text handling can be a little limited; their official stance on the subject is if you want more functionality you should use an external library “like pangocairo that is part of the Pango text layout and rendering library.”  Here’s how to use Pango with Cairo in Python.

This guide takes the form of building a simple Python program that demonstrates a few basic aspects of Pango use.  The full program is in pango_test.py.

There used to be native Python modules for Pango, but they seem to be defunct now.  There are some current modules based on CFFI, but they’re incomplete and I found them difficult to work with.  The approach I like the best is to use the PyGObject module to access Pango, just as a GTK program would.  You’ll also need Pycairo installed, of course.

§ Module Imports

So let’s set up the imports for our program.

import math

import cairo

import gi
gi.require_version('Pango', '1.0')
gi.require_version('PangoCairo', '1.0')
from gi.repository import Pango, PangoCairo

We’ll need the math module for some minor things later.  The cairo module should be obvious.

The gi module is the entry point to PyGObject.  You can check the documentation for more details, but basically you tell it which object versions you want to use, and then import specific libraries’ object definitions.  If you don’t specify the versions, PyGObject will use the latest version available and will print a warning to the console about it (since APIs are not guaranteed to be identical across different GObject release versions).

§ Cairo Surface Creation

Next, let’s set up a Cairo SVG surface to work with.  This should be routine for anyone familiar with Cairo.  We’ll make a surface and draw a white rectangle on it to serve as a background.

surface = cairo.SVGSurface('test.svg', 480, 480)
ctx = cairo.Context(surface)
ctx.set_source_rgb(1, 1, 1)
ctx.rectangle(0, 0, 480, 480)
ctx.fill()

§ PangoCairo Layout Creation

Now we get into the first Pango-specific thing.  The Pango library does text layout and rendering, and there’s a PangoCairo library that serves to translate Pango’s rendering into something Cairo can understand.  (Under the hood, it uses Cairo’s low-level glyphs API, but you don’t really need to worry about that.)

PangoCairo uses a “layout” to manage text rendering.  Roughly speaking, you establish a layout, tell it how to render text (what font, how to justify the text, how to space characters and lines, and so on) and what text to render, and then it gives you the rendered result.

So we start by creating a layout to use.  It takes the current Cairo context as a parameter.

layout = PangoCairo.create_layout(ctx)

§ Font Loading

Next, we’ll load a font.  Fonts are a little complicated in Pango.  The library uses a description of a font as a reference for locating the font on your system.  The description consists of information like the font name, its style (bold, italic, thin, etc.), and its size.  The easiest way to load a font is via Pango.font_description_from_string().  It takes as its parameter a string describing a font.  It will try to match the description given; if it can’t, it’ll fall back to something it thinks is a suitable substitute.

The description string has a syntax of its own, described in the documentation.  You can generally assume that you can just put the font name, style, and a number for the point size in the string and it should work.  I’ve found that sometimes adding the optional comma after the font name can help, as in the example below.  We’re going to use Times New Roman for the font.  But “Roman” is also a style keyword for Pango, so the string “Times New Roman 45” will be interpreted as a request for the Roman style of a font named “Times New”.  Putting in the comma, as in “Times New Roman, 45” lets Pango parse things correctly.

Linux users without the Microsoft fonts installed might want to use Liberation Serif instead of Times New Roman.

Note that the size given is relative to the base unit of the Cairo surface.  For a standard SVG surface, as we’re using, the size will be in points.  If we were using an ImageSurface, the below code would load Times New Roman at a size of 45 pixels tall.

After loading the font description, we tell the layout to use the font we’ve described.

font_description = Pango.font_description_from_string('Times New Roman, 45')
layout.set_font_description(font_description)

We’ll go over an alternate mechanism for loading fonts later.

§ Context Translation

Just to illustrate something, we’ll translate the Cairo context to change where the origin is.  This isn’t strictly necessary—you can just call Context.move_to() before outputting the text—but this demonstrates how you need to handle context translations when working with Pango.

Internally, the PangoCairo layout keeps a PangoContext—which, as it might sound, is the Pango version of a Cairo context.  If the transformation matrix of the Cairo context changes, you need to call PangoCairo.update_layout() to keep the PangoContext in sync.  You also need to call PangoCairo.update_layout() if the context’s target surface changes.

ctx.translate(16, 16)
PangoCairo.update_layout(ctx, layout)

§ Text-drawing Function

We’ll want to render a few strings in the program, and there are a few things we want to do for each string.  So we’ll make a function to render a given bit of text.  The function needs the Cairo context, the PangoCairo layout, and, of course, the text to render.

def draw_text_and_bounds(ctx, layout, text): 

The function will also return some information about the bounds of the text it rendered, but we’ll get to that shortly.

§ Set the Text to Render

Telling the layout what text it needs to render is a simple function call.

    layout.set_text(text)

§ Text Bounds

Example of Text Bounds

Pango gives you a few different measures of what the bounds of the text are.  In Cairo’s toy text API, the point of origin for rendering text is the left side of the text, on the text baseline.  In contrast, Pango’s point of origin is the upper left corner of the box defining the text’s logical extents.

The logical extents of the text are the full area the text occupies, based on the font.  The extents include vertical whitespace to encompass a full line of text, which means having room for ascenders and descenders (whether or not the text being rendered has them) and extra vertical room the font might include (some fonts add vertical whitespace above—and occasionally below—all of the characters).  The width of the logical extents more or less corresponds to Cairo’s TextExtents’ x_advance.  In the image to the right, the logical extents of each string are shown in red.  Note that the first string has two spaces at the end.

Pango also has ink extents for a given piece of text.  The ink extents are the area covered by the specific glyphs being rendered.  This more or less corresponds to the area covered by Cairo’s TextExtents’ x_bearing, y_bearing, width, and height.  In the image to the right, the ink extents are shown in blue.

The function Layout.get_extents() returns two values: the ink extents and the logical extents, in that order.  Each value is a PangoRectangle, which has x, y, width, and height members.  The coordinates are all with respect to the upper left corner of the logical extents.  That means the x and y values for the logical rectangle should always both be zero.  The y value for the ink rectangle will almost always be positive.  The x value for the ink rectangle might be zero, might be positive, and might be negative.  (In the example on the right, the second line has a slightly-negative ink x value, while the third line has a positive x value.)

The values in the rectangles returned by Layout.get_extents() are in Pango units.  You must divide them by Pango.SCALE to get the equivalent Cairo units.  (While Cairo works with floats, Pango works with integers.  In order to get a reasonable amount of precision, Pango scales up its coordinates by a factor of Pango.SCALE in order to facilitate meaningful subpixel operations.)

In Cairo, you always know where the text’s baseline is, because that’s where Cairo starts from when drawing text.  With Pango, you call Layout.get_baseline() to get the distance from the top of the logical extents to the baseline.  As with other Pango functions, the result is in Pango units and must be divided by Pango.SCALE to get Cairo units.  The baseline is shown in orange in the example on the right.

With that in mind, the following code will draw boxes around the text’s various extents, as well as put a small dot at the text’s origin (the upper left corner of the logical extents).

    ink_rect, logical_rect = layout.get_extents()
    baseline = layout.get_baseline()

    ctx.set_line_width(2)

    # Baseline in orange.  Offset to account for stroke thickness. 
    ctx.set_source_rgb(1, 0.5, 0.25)
    ctx.move_to(-8, baseline / Pango.SCALE + 1)
    ctx.line_to((logical_rect.x + logical_rect.width) / Pango.SCALE + 8,
                baseline / Pango.SCALE + 1)
    ctx.stroke()

    # logical rect in red
    ctx.set_source_rgb(0.75, 0, 0)
    ctx.rectangle(logical_rect.x / Pango.SCALE - 1,
                  logical_rect.y / Pango.SCALE - 1,
                  logical_rect.width / Pango.SCALE + 2,
                  logical_rect.height / Pango.SCALE + 2)
    ctx.stroke()

    # ink rect in blue
    ctx.set_source_rgb(0, 0, 0.75)
    ctx.rectangle(ink_rect.x / Pango.SCALE - 1,
                  ink_rect.y / Pango.SCALE - 1,
                  ink_rect.width / Pango.SCALE + 2,
                  ink_rect.height / Pango.SCALE + 2)
    ctx.stroke()

    # Baseline in orange
    ctx.set_source_rgb(1, 0.5, 0.25)
    ctx.move_to(-8, baseline / Pango.SCALE)
    ctx.line_to((logical_rect.x + logical_rect.width) / Pango.SCALE + 8,
                baseline / Pango.SCALE)
    ctx.stroke()
    
    # Origin in dark blue
    ctx.set_source_rgb(0, 0, 0.5)
    ctx.arc(0, 0, 2 * math.sqrt(2), 0, 2 * math.pi)
    ctx.fill()

§ Render the Text

The PangoCairo.show_layout() function is what takes care of outputting the rendered text to the Cairo surface, by way of the Cairo context.  As noted previously, PangoCairo uses the Cairo context’s current position as the upper left corner of the text’s logical bounds.  Since this program relies on using Context.translate() to shift the origin to the appropriate rendering location, we’ll simply draw the text at that (translated) origin.

    ctx.set_source_rgb(0, 0, 0)
    ctx.move_to(0, 0)
    PangoCairo.show_layout(ctx, layout)

§ Return the Logical Bounds

To facilitate text positioning, we’ll have the function return the logical bounds of the text having been rendered.  Keep in mind that the bounds are in Pango units, not Cairo units!

    return logical_rect

§ Actually Render Some Text

Now that the function is set up, let’s draw some text.  This will draw the text “Aa Ee Rr”, followed by two spaces, in the previously-loaded font (45 point Times New Roman).

logical_rect = draw_text_and_bounds(ctx, layout, 'Aa Ee Rr  ')

Following that, we’ll shift the origin down by the logical height of the rendered text, plus a bit of a buffer.  We’ll then render the text “Bb Gg Jj” (which has some descenders) in italic Times New Roman.

ctx.translate(0, 16 + logical_rect.height / Pango.SCALE)
PangoCairo.update_layout(ctx, layout)
font_description = Pango.font_description_from_string('Times New Roman, Italic 45')
layout.set_font_description(font_description)
logical_rect = draw_text_and_bounds(ctx, layout, 'Bb Gg Jj')

Next, we’ll shift the origin down a little farther for the next line of text.

ctx.translate(0, 16 + logical_rect.height / Pango.SCALE)
PangoCairo.update_layout(ctx, layout)

§ Alternate Font Loading Method

When you use Pango.font_description_from_string(), you’re not necessarily guaranteed to get the font you asked for.  If Pango can’t satisfy the request literally, it’ll use a fallback font instead.  (In my experience, the fallback font ignores all additional font characteristics like point size and font weight, which can give substantially less-than-ideal results.)

An alternate approach involves going through PangoFontMap.  You can load a list of all of the fonts on the system with PangoCairo.font_map_get_default(), which returns a PangoFontMap object.  From there, you can use PangoFontMap.get_family() to retrieve a font family by name (as a PangoFontFamily object) and then use PangoFontFamily.get_face() to retrieve a specific font variant within the family.  Face names will be things like “Regular”, “Bold”, “Bold Italic”, and so on.  You can call PangoFontFamily.list_faces() to return a list of all of the faces available for a family.

If you use this approach and the requested family or face is unavailable, the relevant function will return None instead of an object.  You can use this to be precise about your font loading rather than relying on Pango’s fallback mechanism.

Once you have a font face, you get its description with PangoFontFace.describe(), which returns a PangoFontDescription object.  After setting the font description size (in Pango units, so you’ll probably need to multiply by Pango.SCALE), you can set the font on a layout using Layout.set_font_description(), just as when we loaded a font description from a string earlier.

Here we’ll load 45 point italic Times New Roman using this alternate method.

font_map = PangoCairo.font_map_get_default()
font_family = font_map.get_family('Times New Roman')
font_face = font_family.get_face('Italic')
font_description = font_face.describe()
font_description.set_size(45 * Pango.SCALE)
layout.set_font_description(font_description)

Note that the final program, linked above and below, contains some extra error handling here in case the specified font family or face is not available.

Also note that the font description being loaded is still a Pango-created summary of the font.  In my experience, this approach can be a bit more precise than Pango.font_description_from_string(), but I’ve still run into problems.  I have one font family on my system where the specific font names are a little messed up and several different weights of the italic faces have the same name.  I can select the exactly-correct PangoFontFace object using the above process, but the summary it generates as a PangoFontDescription leads to the wrong weight being loaded.

Anyway, now the font’s been loaded, let’s render some more text.  Just for fun, we’ll do Hebrew, which is written right-to-left.

logical_rect = draw_text_and_bounds(ctx, layout, 'אָלֶף־בֵּית עִבְרִי')

Note that some of the diacritics here are actually a little outside the logical text bounds (unless you’ve changed to a font that handles them differently).

Note also that even though the text runs right-to-left, Pango still uses the left side of the rendered text as its origin.  This ensure consistent placement regardless of the text direction.  (And, indeed, you can mix together text with different writing directions and Pango will lay everything out in a reasonable way.)  If you were aligning several lines of right-to-left text (and didn’t want to just pass a string with newlines to Pango and let the library figure it out), you would need to use the text extents to determine how to position Pango’s logical origin to the left of the text’s right margin.

§ Conclusion

The completed program, with some comments and a little error handling, is at pango_test.py.

If you’re used to Cairo’s toy text API, Pango isn’t too dissimilar, although there is another object to keep track of, and font loading is a bit different.  The main thing I had to get used to—and it didn’t take too long—was the different origin for text rendering and the different way Pango has that it thinks about text extents.