Geotagging at SFO Museum, Part 3 – What Is the Simplest Thing?

This is a blog post by aaron cope that was published on April 27, 2020 . It was tagged sfo, collection, geotagging and golang.

photograph: Spruce Goose. Paper, ink. Gift of Frank A. Norick, SFO Museum Collection. 2016.042.022.

The is the third of an 11-part blog post about geotagging photos in the SFO Museum collection. At the end of the second post I said:

In the next post we’ll use the go-http-leaflet-geotag middleware package to start building our geotagging application.

If you're not sure what the "middleware" refers to please have a look at the last blog post for a discussion of the term.

And this is what that application looks like:

It looks pretty similar to the screenshot for the example application included with the go-http-leaflet-geotag package we talked about in the last post:

There are a few layout changes but most of the differences are under the hood, and unseen, making use of functionality provided by a number of additional “middleware” packages:

  • The go-http-bootstrap package. This bundles Bootstrap, a framework for developing responsive and mobile websites.
  • The go-http-leaflet package. This bundles Leaflet.js, a framework for web-based mapping applications.
  • The go-http-tangramjs package. This bundles Tangram.js, a framework for styling and rendering data in web-based mapping applications. It also exports a handful of default Tangram map styles which define the cartography and visual elements used by Tangram.js to render tile data provided by Nextzen, a free and open provider of geographic data for web-based mapping applications.
  • The go-http-tilezen package. This can be used to proxy and cache local copies of Nextzen tile data.

Here’s an abbreviated example of how that fits together in code:

import (
	tz_http ""

func main() {

	mux := http.NewServeMux()

	tiles_handler, _ := tz_http.TilezenProxyHandler(...)
	mux.Handle("/tiles", tiles_handler)
	bootstrap_opts := bootstrap.DefaultBootstrapOptions()

	tangramjs_opts := tangramjs.DefaultTangramJSOptions()
	tangramjs_opts.Nextzen.APIKey = "your-nextzen-api-key"
	tangramjs_opts.Nextzen.StyleURL = "/tangram/styles/"
	tangramjs_opts.Nextzen.TileURL = "/tiles/{z}/{x}/{y}.mvt"

	geotag_opts := geotag.DefaultLeafletGeotagOptions()

	// this is where most of the logic for the actual
	// geotagging application lives
	editor_handler, _ := www.EditorHandler(...)
	editor_handler = bootstrap.AppendResourcesHandler(editor_handler, bootstrap_opts)
	editor_handler = tangramjs.AppendResourcesHandler(editor_handler, tangramjs_opts)
	editor_handler = geotag.AppendResourcesHandler(editor_handler, geotag_opts)

	mux.Handle("/", editor_handler)

With the exception of the go-http-leaflet-geotag handler and the application-specific code highlighted in blue this is almost exactly the same code we used to develop the geocoding application described in the Using the Placeholder Geocoder at SFO Museum blog post. And that’s the point.

Bootstrap (started by and for Twitter in 2011), Leaflet) (also started in 2011 and used by almost everyone who isn’t Google Maps), Tangram and Tilezen (started in 2013 and 2015, respectively, as part of the Mapzen project) represent decades of effort and problem-solving and finessing offered to the world in a spirit of generosity. We don’t need, or have the time, to reproduce that work.

Being web-based technologies we could also have simply downloaded each project locally and bundled them directly in our geotagging application. It’s a chore, sometimes a nuisance, to have to do that for every project though.

The weight and impact of these go-http- middleware packages, each of which SFO Museum has developed internally or actively contributed to, shouldn’t be overstated. They are “tools of convenience” designed to make integrating external functionality in a bespoke application quick and easy.

They are simple tools but they are part of our attempt to make good on something I said in the first blog post in this series:

The cultural heritage sector needs as many of these small, focused, tools as it can produce. It needs them in the long-term to finally reach the goal of a common infrastructure that can be employed sector-wide. It needs them in the short-term to develop the skill and the practice required to make those tools successful. We need to learn how to scope the purpose of and our expectations of any single tool so that we can be generous of, and learn from, the inevitable missteps and false starts that will occur along the way.

As of this writing our geotagging application is published in an unimaginatively named package called go-www-geotag. A better name is probably in order but this one will do for the time being.

We don’t yet provide pre-compiled binary versions of the application so in order to fetch and build the application you’ll need to have a tool, like wget or simply your web browser, to download the source code and the Go programming language for building it:

# As I write this the releases for the go-www-geotag package change often
# so the best thing is to vist
# and download the most recent release.

$> wget{X}.{Y}.{Z}.zip
$> unzip v{X}.{Y}.{Z}.zip

$> cd go-www-geotag
$> go build -mod vendor -o bin/server cmd/server/main.go

To start the application you would type:

$> ./bin/server \
	-nextzen-apikey {NEXTZEN_API_KEY}

2020/04/15 08:56:56 Listening on http://localhost:8080

In order to use the application you’ll need to sign up for a Nextzen API key and pass that key in with the -nextzen-apikey flag. And then load http://localhost:8080 in your web browser. That’s it!

The application is still in its early days. Both the user interfaces and user experiences lack polish and nuance while we figure out what the underlying scaffolding needs to support.

I call this the “building with two-by-fours” stage of development to highlight the importance of not just building things quickly but equally being able to disassemble and rearrange them just as easily. Sometimes this happens at the expense of elegance and finish but that doesn’t diminish their importance or a commitment to address those things. We’re just not there yet.

Finally, do you remember the geocoding application I mentioned above? In the next post I will demonstrate how to integrate it with our geotagging application.