Skip to content
Open Source Mapping for News: Reuters

Open Source Mapping for News: Reuters

The news service Reuters was looking to build an open source web map that could work for a variety of story types, with a focus on automated data. As a graphic designer, cartographer, and visual journalist, I brought to this project a unique perspective on weaving together the range of considerations required for a seamless mapping experience. I worked with them to explore the current open source options and to design and build a tool that met their technical requirements while maintaining an aesthetic look and feel that could fit on their pages. In the following text I will walk through the process of designing and building the map system. 

Open Source Ecosystem

Protomaps

After a general survey of the interactive web mapping landscape, we found that Protomaps, an open source interactive web map fit the bill. It has seen widespread adoption as newsrooms begin to move away from subscription-based mapping services towards open source options.

Protomaps (versus a subscription-based service) offers control and reduced cost. An organization can store all of the geo-data in a single (massive) file hosted on their own static file server, as opposed to more complex tile servers of the past. Map Libre, a front end web mapping tool feeds only what is in view leading to efficient and fast use of data. In addition to simple storage, we could design a custom base map tailored for the variety needs.

PMTiles

At the heart of the Protomaps setup is the PMTile, a format to house tiled geo-data. This is the raw, unstyled geographic data: roads, lakes, buildings, parks, labels, etc. All of this information is packaged up into a single PMTile and stored on a server of Reuters' choice. 

This image below, showing the Chicago area is an example of all of the geo data that might be in a typical PMTile. Keep in mind that just because the data is there doesn't mean that it has to be displayed at all zoom levels. Part of the job of the designer is to make those decisions, what is shown when so that the reader seamlessly moves through the geographic space:

I found the PMTile viewer a helpful tool for analyzing what is in a pmtile and what the layers are named:

Assembling the Geo-data

Building the basemap tiles: Planetiler & OpenMapTiles

To assemble the vast amount of geo-data needed to power a map of the world from the globe on down to your corner store, we used the OpenMapTiles schema. OpenMapTiles takes publicly available data, OpenStreetMap and Natural Earth Data and curates the what and when of how that data comes onto your screen, optimized for different zoom levels. Loading every bit of OpenStreetMap data for the average user just isn't necessary or performant. This process is a massive lift, requiring vast amount of processing.

Initially we set out to build the planet-scale PMTile through the OpenMapTiles process but soon learned (after multiple 24-hour+ heartbreaks) that there are better ways. Enter, Planetiler, a fast and efficient way to bake out planet-scale PMTiles. The Planetiler process, using the OpenMapTiles scheme can generate planet-scale PMTile file in about 4 hours. The single file would come in at around 80gb. Hallelujah. 

Simplification: Buildings

When building out the basemap, we analyzed what data the OpenMapTiles scheme included, and made decisions to leave out some data that weren't necessary for our purposes. We also tweaked the simplification settings so that the map wasn't fed unnecessary levels of detail. The downside to this approach was that at the highest zoom level, buildings took the brunt of simplification and started to look wonky. As a workaround, we output two main PMTile files for the basemap, one with all the data except buildings, and one with only buildings. This way, we can keep the level of detail in the buildings without sacrificing performance elsewhere. 

Building Custom Tiles: Tippecanoe

For smaller custom datasets such as disputed borders, custom labels, or Reuters-specific data, Tippecanoe provided a flexible tool for creating specialized PMTiles. While I spoke of a giant PMTile earlier, this whole setup can handle multiple PMTiles easily. I felt that having a smaller, more customized PMTile would allow the team the opportunity to change details in the basemap (disputed borders for instance) without having to re-bake the full basemap. 

Generally Tippecanoe is run from the terminal. Here's an example of it pulling a number of json and geojson files together into reuters.pmtiles. Tippecanoe can handle multiple file input types beyond json and geojson.

Design

At its core, the map needed to be a supportive element: neutral, authoritative while also still nice to look at. The traditional design elements, color, line, typography are a part of this toolbox. But an interactive map exists on many levels, how does it look at different zoom levels, on desktop, on mobile? How and when do elements appear on the page? How does it feel when moving between different states? 

Styling the map: Maputnik 

Maputnik is an open-source web interface used to adjust styles for MapLibre—similar to Mapbox Studio. It provides a graphical interface for loading PMTile and other geo data sources, fonts, and styling every element of the map. Typically, at the end of this process, the output is a json file that MapLibre uses to render the raw geo-data as 'the basemap.' Generally, any further data loads onto the map after the basemap has finished loading. 

Approaching the styling of a slippy map is zoom-based, meaning that the style of particular maps elements are dependent on how far the user is zoomed into the map. Different zoom levels require different considerations. When looking at the full globe, one does not to see the label for their street. And likewise at the street level, country and state labels generally aren't necessary. It's a push and pull to make sure there is the right amount of context at a given zoom level, without crowding the map, knowing that there will be additional information layered on top of the map in the news stories the map will serve. 

What is the context that needs to be there to let people know where it is they're looking at? Is it the natural landscape? Population centers? Buildings? A combination? When a map is continuously moving, the visual element used to orient readers constantly must shift.

At a global scale, you need land masses and large bodies of water to orient yourself, country boundaries and major country labels. You always run the risk of overcrowding the map, again keeping in mind that a visual journalist will be populating this map with the thing they are conveying to the reader.

At a regional scale, you get into sub-national boundaries, states and provinces. But I also felt that population centers at this stage becomes important, where are the people are. So I emphasized simplified urban areas even above states a bit.

Zoom in a bit further and you need some more detail, blobby urban areas won't cut it. Highways, major roads and city/town labels begin to orient the reader.

Further on, more detailed streets, finer rivers, streams, lakes come into view. When you're at an almost neighborhood level, buildings become a way of orientation. 

Typography

Map libre allows for custom fonts. Map labels require fonts in PBF (Protocol Buffer) tile format. Reuters has its own proprietary font, and we were able to use MapLibre's Font Maker tool to convert it to a PBF file which Reuters can then host.

Projections

The default projection of the map is the web pseudo-mercator projection. This is standard across slippy maps but is highly distorted the further you get from the equator. For this reason, we opted to have the globe view as the standard projection.

Custom Raster Layers: Hillshade

Beyond vector data, we can add raster tiles (as well as video!) to the map for effects like hillshades. I felt that a subtle hillshade layer provides geographic context without being visually overwhelming. Below is the flattened version of the hillshade which then is textured over the globe:

Maptiler offers high resolution hillshades to high zoom levels for a subscription fee. This was overkill for the purposes of the general Reuters' basemap I was building. They can always add this service to their basemap in the future if a story calls for it. 

When creating raster files for planet-scale mapping:

Creating raster tiles:
  • Create raster in EPSG:3857 projection (Pseudo-Mercator)
  • For zoom levels 0-6, render at least 16384×16384 pixels
  • Extent: -180 to 180° longitude, ±85.0511° latitude
  • Use GDAL to generate XYZ tiles: gdal2tiles.py -z 0-6 -w none --tmscompatible input.tif output/
  • Upload tiles to static hosting and reference in Maputnik
  • The raster layer can be turned off in the style.json (that controls the basemap styling) by changing its visibility property

Customizations

We identified a few areas where the off-the-shelf processes needed a hand:

Disputed Borders

Borders can shift, different parties assert differing claims, they can vary from organization to organization, country to country. Reuters has a standard that they maintain, and it was important that this was consistent across all zoom levels. In the OpenMapTiles scheme that we used to build out the basemap, there was a data set shift between lower and upper zoom levels, from Natural Earth Data to OpenStreetMaps data. Unfortunately this led to inconsistency in the disputed borders. To maintain consistency, we added the Reuters-approved disputed borders data as part of the custom PMTile.

Water Body Labels

In the hierarchy function of how labels show up on the map, at times the ones you want to be there aren't always there when you want them to be. So to be sure that the important water body labels were there when we wanted, we supplemented the off-the-shelf data with a custom label data set made from Natural Earth Data. 

Polar Caps

MapLibre's globe projection only extends to ±85.0511 degrees latitude. Beyond these limits, the Pseudo-Mercator projection breaks down, creating visual artifacts. We added simple polygon features that mask these areas with colors matched to the surrounding ocean and ice.

Language

In the OpenMapTiles schema, labels are mostly handled by OpenStreetMaps data. This generally works well, but there are issues with data type consistency across languages. For instance, some labels in Japanese are Chinese pronunciation of Japanese words. Also, sometimes there isn't any data for a particular category, so fallbacks are necessary. Here is an example of labels for highways that ranks which label categories to look for and provides fallbacks in case there isn't a label available: 

{
  "id": "highway_name_motorway",
  "type": "symbol",
  "metadata": {
    "mapbox:group": "b6371a3f2f5a9932464fa3867530a2e5"
  },
  "source": "reuters",
  "source-layer": "transportation_name",
  "filter": [
    "all",
    ["==", "class", "motorway"],
    ["==", "$type", "LineString"],
    ["!=", "class", "ferry"]
  ],
  "layout": {
    "symbol-placement": "line",
    "symbol-spacing": 350,
    "text-field": [
      "coalesce",
      ["get", "name_int"],
      ["get", "name:en"],
      ["get", "name_en"],
      ["get", "route_1_ref"],
      ["get", "route_1_name"]
    ],
    "text-font": [
      "Knowledge2017 Medium"
    ],
    "text-max-angle": 30,
    "text-pitch-alignment": "viewport",
    "text-rotation-alignment": "map",
    "text-size": {
      "stops": [[10, 8], [16, 11]]
    },
    "text-transform": "uppercase",
    "visibility": "visible",
    "text-letter-spacing": 0.2
  },
  "paint": {
    "text-color": "rgba(143, 143, 143, 1)",
    "text-halo-color": "rgba(255, 255, 255)",
    "text-halo-width": 1,
    "text-translate": [0, 0],
    "text-opacity": {
      "stops": [[6, 0.5], [14, 0.7]]
    }
  }
}

Displaying the map

Once the basemap data and style have been set, and the data that will appear on the map is gathered, it's time to assemble into a map on the page. 

MapLibre GL

MapLibre GL is the open-source renderer that brings these elements together. Anyone who has worked with Mapbox products will be familiar with its functionality. User interactions happen in the realm of MapLibre, and there are robust options how to use the map for your specific purposes. It's worth looking through MapLibre's examples to get a sense of what types of functionality and user interactions are possible with the maps.

Create the map

Here we create the map object and set some initial conditions like where the map is centered and the level of zoom. We also set the style of the basemap with the json file that we created in Maputnik.

const map = new maplibregl.Map({
   "container":"map",
   "style":"https://example.com/style.json",
   "center":[-98.81,38.76],
   "zoom":4,
   "projection":"globe"
});
Loading Data

I view the map as two general categories, static and dynamic. The static map is the basemap, generally unchanging, the base in which story-related data sits on top. This is what we styled in Maputnik. Below is an image of drought data loaded as a geojson onto the basemap. 

Here is the code that loads that data:

map.on("style.load",
"() =>"{
   "map.addSource(""drought",
   {"type":"vector",
      "url":"pmtiles://https://srm-pm-prototype.s3.us-east-1.amazonaws.com/us-drought-monitor.pmtiles"
   }");

map.addLayer("{
      "id":"usdrought",
      "type":"fill",
      "
      "source":"drought",
      "source-layer":"usdroughtmonitor",
      "layout":{
         "visibility":"none"
      },
      "paint":{
         "fill-color":[
            "interpolate",
            ["linear"],
            ["get","DM"],
            0,"#ffedd5",7,"#dc4300"],
         "fill-opacity":{
            "stops":[
               [1,0],
               [2,.5]
            ]
         }
      }
   },
   "firstSymbolId
        );"
}
Video
Video can be textured over to great effect. For a full globe video, create a video in the Pseudo-Mercator projection, similar in extent to the hillshade raster image described above:

Adding a video requires loading a source and then loading the layer after the basemap has loaded on the page. Labels can sit on top of the video, similar to other data:
//Video source
map.addSource('video-source', {
                      type: 'video',
                      urls: [' VIDEO SOURCE URL'], 
                      coordinates: [
                          [-180, 85.0511],   // top-left
                          [180, 85.0511],    // top-right  
                          [180, -85.0511],   // bottom-right
                          [-180, -85.0511]   // bottom-left
                      ]
                  });

//Add video layer
map.addLayer({
                      id: 'video-layer',
                      type: 'raster',
                      source: 'video-source',
                      paint: {
                          'raster-opacity': .3 
                      }
                  },
                  firstSymbolId
                  );
Labels Over Data
For better legibility, display labels on top of data layers. Here's a function that pulls the label layer and places on top of a data layer. Call this function when adding in a data layer:
let firstSymbolId;
for (let i = 0; i < layers.length; i++){
   "if (layers"[
      "i"
   ]".type ===""symbol"")"{
      "firstSymbolId = layers"[
         "i"
      ]".id;
break;"
   }
},
"calling""firstSymbolId""map.addLayer("{
   "id":"past-windswath-pmtile",
   "type":"fill",
   "source":"past-windswath-pmtile",
   "source-layer":"pastwindswath"
}
},
"firstSymbolId
);"
Map in use

Reuters began rolling out the basemap this fall with weather-related stories. Here you can see how the basemap we designed integrates with automated hurricane path data designed by the Reuters graphics team.


This example below shows additional elements that can be added to a map, including a scale bar and small locator map.

Images courtesy of Ben Welsh.

Open source release and tutorial

Ben Welsh, News Applications Editor at Reuters, has written a fantastic tutorial for assembling your own free interactive map, using the same processes we used for Reuters. Read the post here.