Geotagging at SFO Museum, part 2 – First Steps

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

negative: Pan American World Airways, Pacific-Alaska Division. Negative. Gift of William E. Talbott, SFO Museum Collection. 2012.151.156.

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

[W]e set out to build a simple and extensible stand-alone web application for geotagging images. Ultimately, as you’ll see in the blog posts to follow, this is an application designed by and for SFO Museum but we hope we’ve done things in a way that allows another institution to adapt and rearrange its core building blocks to suit their own needs.


We’ve chosen the Go programming language for developing this application. Go is not necessarily the first or always the best choice for web applications so here are the pros and cons that informed our decision:

  • Go has a steeper learning curve than other programming languages. Until you get the hang of it Go can seem profoundly weird.
  • Go is a strictly-type language that needs to be compiled to run which means that it is often just a little bit slower to do the same thing than it might in another language.
  • Writing web applications that need to change quickly and often in Go is not very much fun because of the first two points and because Go’s built-in templating support is not as flexible as others.
  • On the other hand this application is designed to be pretty simple and not change a lot. Much of the logic is contained in the application’s JavaScript layer which can be edited locally and tested quickly in a web browser.
  • Go makes it very easy to write the server-side components of a web application and ships with built-in support for creating web servers.
  • Go has robust support for serving web applications locally, in a traditional server setting and in the cloud.
  • Go allows you to compile and serve all of your web application’s assets (HTML templates, Javascript and CSS files and so on) in to and from the application itself.
  • Go applications can be pre-compiled in to operating system specific binary applications that don’t require any additional steps or dependencies to run.

This last point is really important. In the end we may deploy this application for staff as a hosted website on the internet but we would like to have the ability and the flexibility for staff to also run the application locally, from their desktop, regardless of whether they are using Windows or a Mac or even the Linux operating system. The majority of museum staff are not developers and won’t know how or be able, or want, to install the external dependencies that might be necessary for an application written in another programming language to run.

Ultimately, the Go part of our application is little more than a web server wrapped around a standard HTML + JavaScript + CSS web application that could be run in any number of environments. That’s by design so that the “meat” of the application doesn’t get painted in to a corner of any single programming language or platform.

There is, in fact, more to the Go part of the application than a web server and we’ll cover those details in later blog posts. At the same time, if you’ve ever had to set up and maintain a web server by hand then having Go take care of those details and wrap everything up in a canned application that runs by itself is a welcome development. So that’s why we chose Go for this application.

timetable: BEA (British European Airways). Paper, ink. Gift of the William Hough Collection, SFO Museum Collection. 2005.132.065.004.

With that in mind, the first thing we did was write what’s referred to as a “middleware” handler for the Leaflet.GeotagPhoto plugin we talked about in the last blog post.

In his blog post Making and Using HTTP Middleware Alex Edwards describes middleware this way:

When you're building a web application there's probably some shared functionality that you want to run for many (or even all) HTTP requests. You might want to log every request, gzip every response, or check a cache before doing some heavy processing.

One way of organising this shared functionality is to set it up as middleware – self-contained code which independently acts on a request before or after your normal application handlers. In Go a common place to use middleware is between a ServeMux and your application handlers, so that the flow of control for a HTTP request looks like:

ServeMux => Middleware Handler => Application Handler


In Go, “ServeMux” is the name of the code that routes requests for a URL to a specific piece of application code, also called a “handler”.

Our middleware, which is published in the go-http-leaflet-geotag package, bundles all the files and dependencies used by the Leaflet.GeotagPhoto plugin and makes them available locally to a web application. It can also be configured to intercept requests for webpages and inject all the relevant markup so that an application can start to use the Leaflet.GeotagPhoto JavaScript library.

Here’s an abbreviated example of what this looks like in code:

import (
	"github.com/sfomuseum/go-http-leaflet-geotag"
	"net/http"
)

func main(){

	mux := http.NewServeMux()

	// Make all Leaflet.GeotagPhoto files available
	geotag.AppendAssetHandlers(mux)

	camera_handler, _ := PageHandler(t, "camera")

	// Inject markup for using Leaflet.GeotagPhoto files in to a web page
	geotag_opts := geotag.DefaultLeafletGeotagOptions()
	camera_handler = geotag.AppendResourcesHandler(camera_handler, geotag_opts)

	mux.Handle("/camera/", camera_handler)
}

Let’s imagine the output of the PageHandler code above looks like this:

<html>
	<head>
		<title>PageHandler</title>
	</head>
	<body>
		<p>Hello world</p>
	</body>
</html>

After it is processed by the geotag.AppendResourcesHandler middleware code it looks like this:

<html>
	<head>
		<title>PageHandler</title>
		<link rel="stylesheet" type="text/css" href="/css/leaflet.css" />
		<link rel="stylesheet" type="text/css" href="/css/leaflet.fullscreen.css" />
		<link rel="stylesheet" type="text/css" href="/css/Leaflet.GeotagPhoto.css" />
		<link rel="stylesheet" type="text/css" href="/css/highlight.min.css" />				
		<script type="text/javascript" src="/javascript/leaflet.js.js"></script>
		<script type="text/javascript" src="/javascript/leaflet.fullscreen.min.js"></script>
		<script type="text/javascript" src="/javascript/leaflet-hash.js"></script>		
		<script type="text/javascript" src="/javascript/Leaflet.GeotagPhoto.js"></script>
		<script type="text/javascript" src="/javascript/highlight.min.js"></script>
	</head>
	<body>
		<p>Hello world</p>
	</body>
</html>

And the geotag.AppendAssetHandlers(mux) method is what adds all those /javascript/... and /css/... URLs to the mux (the ServeMux mentioned above) so that requests for those files succeed.

That’s it.

The go-http-leaflet-geotag middleware layer doesn’t define how your application uses the Leaflet.GeotagPhoto extension. The point of this middleware layer is to make adding the Leaflet.GeotagPhoto extension to your application as easy as three extra lines of code.

The default example application that's included with the go-http-leaflet-geotag code base simply recreates the example application in the Leaflet.GeotagPhoto code base.

In the next post we’ll use this piece of middleware to start building our geotagging application.