Improving Park Label Placement with Dymo and TileMill

Posted on 28 Jun 2012 by Mamata Akella

Back in March, we were asked by the NPS Office of Communications to design and develop a web map that displayed the National Parks which were good viewing places for the May 20th annular solar eclipse. Our goal was to make a simple web map that displayed the path of the annular eclipse, along with the parks which would have a view of full annularity (parks symbolized in yellow) and a view of the partial eclipse (parks symbolized in orange). The map we created was embedded in the Natural Resources Program Annular Eclipse website.

To create and serve this web map we used a combination of technologies: ArcGIS and QGIS for data processing, TileMill for design, Dymo for labeling, TileStream to serve the tiles, and the NPMap library to develop the web map.

This post specifically covers how we used the open source labeling tool, Dymo, to improve label placement on the map.

The challenge

We encountered one of the biggest challenges for this project while attempting to label the densely-packed National Parks in TileMill. As highlighted below in red, using TileMill’s labeling engine resulted in overlapping labels that made park names illegible.

Picture of TileMill Labels

While looking for a better labeling solution, we came across Dymo, a Python script that was created by Michal Migurski and GeoIQ while designing GeoIQ’s Acetate basemap.

Dymo ”converts lists of cities (or, in our case, National Parks) with included font information into GeoJSON point and label files suitable for use in geographic rendering.” To summarize: Dymo takes an input CSV file and creates a GeoJSON file based on a process it runs called simulated annealing. This process finds the best non-overlapping position for each label around a point. The GeoJSON files produced by Dymo can be added to TileMill, and used to add labels to tilesets.

Using Dymo, we were able to avoid label overlaps and improve the overall legibility of park names dramatically. The following image shows labels and parks for the same area as the first image after running Dymo.

Picture of Dymo Labels

Note: We only ran Dymo on our National Park labels and not on our cities or states. Because of this, you’ll notice there are park labels overlapping with both city and state labels on the map. In our more recent maps, we’ve run Dymo on all labels, avoiding any overlaps between labels.


Here’s the process we went through to place the National Park labels:

1. Install Dymo

To install Dymo, follow the directions posted in the GitHub repository.

2. Data preparation

Note: A portion of the data preparation process outlined below is for ArcGIS Desktop. The same process can be done in QGIS. UPDATE: Thanks to Nathaniel Kelso for these instructions for QGIS.

The first step was to prepare our National Park point data (which was in Shapefile format). This was a three part process: First, we selected, by scale, which labels would appear, then we added additional attributes using ArcGIS and third, we created a CSV file for each zoom level.

  • We selected which parks should be labeled at each scale by using two attributes: the quality of the view the park would have of the solar eclipse and the park’s area.
  • Dymo requires latitude and longitude coordinates for each feature. We added this information to our National Park point dataset in ArcMap using the following steps:
    • Add two fields to the attribute table: “latitude” and “longitude”, in the "Add Field" dialog. Set “Type” to “Double”.
      add field dialog
    • Right-click on both of the newly-added field and select “Calculate Geometry”. In the “Calculate Geometry” dialog, use the dropdown to calculate the “Y Coordinate of Point” for the “latitude” field and the “X Coordinate of Point” for the “longitude” field, ensuring that you set “Units” to “Decimal Degrees”.
      calculate geometry dialog
  • Once the latitude and longitude coordinates are calculated, the next step is to export a set of given records by zoom level. To do this, we wrote a Definition Query in ArcMap that selected only the National Parks we wanted to label at a given zoom level. Once the records were selected, we exported the resulting shapefile into our project directory. (To export a shapefile from ArcMap right click on it in the Table of Contents and select Data>Export Data). For our map, we had National Park labels at zooms 5-7. This means we had three shapefiles -- one for each zoom level we were labeling.

3. Prepare a CSV to run with Dymo

Dymo requires the following fields in each zoom level’s CSV file:

name the name of the feature you want to label
latitude the latitude, in decimal degrees, of the point feature
longitude the longitude, in decimal degrees, of the point feature
font size the font size of the label you want placed
font file the font type and location of the font file
point size if you are symbolizing points to go along with your labels, the size of the points
preferred placement if there is a particular label you want placed in a specific location around a point, you can add it here (example: top, right, left, etc.)

You can also add a population field if you want to add hierarchy to your labels. This is especially useful for city labels where categories of city labels (large, medium, small) are drawn using different font sizes. For this map, we did not implement any hierarchy for park names; they all were labeled using the same font size.

To prepare our CSV, we opened the DBF attribute information of each zoom level’s Shapefile in an OpenOffice Spreadsheet, formatted it accordingly, and saved it as a CSV.

Here is a portion of what our CSV for zoom level 5 of the Solar Eclipse web map looked like:

Picture of CSV for Dymo

4. Run Dymo

When you install Dymo, folders are created in your working Dymo directory. The data folder is where each scale level’s CSV should be placed. Place a copy of the font you are using for your labels in the fonts folder.

In your Terminal window, cd to Dymo:

cd /Development/Dymo

Then run Dymo. We ran the following command in Terminal for zoom level 5:

python -z 5 --minutes 2 --labels-file labelszoom5.json --places-file pointszoom5.json data/zoom5.csv

Here’s a basic description of the parameters. More information on the parameters can be found in the Dymo documentation:

-z this defines the zoom level you are generating labels for, and should be adjusted each time you run the tool for a new zoom level
--minutes this defines the amount of time the tool will run to find the best placement for labels, and can be adjusted if Dymo doesn't find an ideal placement for your labels within the default of 2 minutes
--labels-file the name of the output file
--places-file the name of the output point file

The tool creates two GeoJSON outputs that are placed in the root Dymo folder. Based on the information we entered in the command above, our two output files will be labelszoom5.json and pointszoom5.json.

The labelszoom5.json file contains the rectangular area in which the label has been placed given the font, font size, point size, and placement information from the CSV (the purple rectangles seen around each label in the image below). The pointszoom5.json file contains the center points of places successfully placed by Dymo. If you want to place point symbols along with your labels, you can use this file.

Dymo output

5. Symbolize the files in TileMill

Next, we brought the Dymo output into TileMill:

  • Add labelszoom5.json to TileMill, using the "Add layer" dialog
    Adding GeoJSON to TileMill.
  • We wrote the following Carto style rule to symbolize our labels in TileMill at zoom 5:
    #labelszoom5 [zoom=5] {
      text-face-name: 'Century Gothic Bold';
      text-fill: #6BE3A1;
      text-size: 10;
      text-halo-radius: 0.75;
      text-halo-fill: #3C3C3C;
      text-allow-overlap: true;

You’ll notice that the font style and size information from our CSV is used in the style rule. We’ve also defined the color of the labels and applied a small halo. The text-allow-overlap property ensures that TileMill will allow all of our labels to draw even if there is another feature close by. Based on these definitions, the labels for zoom 5 look like this (clean and without overlaps!):

Picture of map at zoom 5.

You should repeat the steps outlined above for each zoom level.

6. Manual editing (optional)

There may be some instances where editing the location of labels is necessary. To do this, add the GeoJSON output from Dymo to QGIS. Next, right-click on the file in the Table of Contents and select “Save as…” In the “Save vector layer as…” format drop-down, select “ESRI Shapefile”.

Saving as a Shapefile in QGIS.

Once the Shapefile is saved, you can add it to QGIS (or ArcGIS) and begin editing the position of the rectangular labels.


In this post, we’ve described the process we use to label our maps using Dymo and TileMill. We are extremely satisfied with the labeling results we get using this tool, especially when compared to the default labeling available in the current version of TileMill. Dymo becomes even more indispensable for us when generating labels at smaller scales where placement is even more difficult. We currently use Dymo for label placement for all of our web maps designed in TileMill.