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.

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.

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.
Process
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”.
-
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”.
-
Add two fields to the attribute table: “latitude” and “longitude”, in the "Add Field" dialog. Set “Type” to “Double”.
- 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:

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 dymo-label.py -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.
5. Symbolize the files in TileMill
Next, we brought the Dymo output into TileMill:
-
Add
labelszoom5.jsonto TileMill, using the "Add layer" dialog
-
We wrote the following Carto style rule to symbolize our labels in TileMill at zoom 5:
#labelszoom5 [zoom=5] { text-name:'[name]'; 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!):

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”.

Once the Shapefile is saved, you can add it to QGIS (or ArcGIS) and begin editing the position of the rectangular labels.
Conclusion
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.