58

I'm trying to draw maps similar to these in a LaTeX pdf document:

enter image description here enter image description here enter image description here

I want to auto-generate them from a list of pairs (country,color) to determine the colors.

I have found the package pst-map2dII in pst-geo, however its documentation is from 2004, is in French and the tutorial examples in it no longer seem to work (or I failed to understand something important in the French part).

Is this package still working somehow, and if so how do I get a basic version to work? Either an English manual or a simple tutorial example similar to the world map above, would greatly be welcomed. If not, other packages or methods to auto-generate them outside LaTeX would be equally welcome.

percusse
  • 157,807
user53964
  • 865

4 Answers4

83

Here's a TikZ picture producing the USA map. I produced it as follows:

  1. I downloaded the svg map from wikipedia
  2. I used Inkscape to translate the svg to tikz code (in verbose mode)
  3. the verbose mode annotated the paths with the abbreviated names of the states so I just copied the names and put them as the style of each path (you can do it with a regex)
  4. I defined a state style and a style for each state.

You can repeat the same steps starting from https://commons.wikimedia.org/wiki/File:BlankMap-World6.svg to get a World map.

Now you can color colorado (or modify many aspects of its rendering) just by redefining the CO style. You can wrap all this in a macro if you want with an optional style parameter that you can use to overwrite the state's styles.

The code is too long for this answer I posted it as a gist at

https://gist.github.com/bordaigorl/fce575813ff943f47505

Update

The Gist above now contains two files: USAmap.tex and main.tex. The first defines a command \USA with an optional argument. The main.tex file shows how to use it:

\documentclass{standalone}
\usepackage{tikz}

\input{USAmap.tex}

\begin{document}

\begin{tikzpicture}
\USA[every state={draw=white, ultra thick, fill=black!10},CA={fill=blue},NY={fill=red}]

\draw[dashed] (TX.center) -- (CA.center) -- (AL.center);
\node at (CO) {CO};
\end{tikzpicture}

\end{document}

generates the following

preview

The main addition is the definition of styles to control the appearance of each state and the creation of (approximate) nodes for each state so you can for example attach labels to states or draw paths etc. The only drawback is that the shape of the node is the bounding box of the path of the state (for simplicity and performance reasons) so the center may lay outside it (see FL for example) but it should be enough to give you an idea of what can be done with this approach.

The styles of the states can also be set globally using tikz as in

\tikzset{set state val/.style args={#1/#2}{#1={fill=blue!#2}}}
\tikzset{set state val/.list={CA/100,FL/80,TX/55,NY/20}}

which would interpret the list {CA/100,FL/80,TX/55,NY/20} as couples state/percentage and assign to each mentioned state a shade of blue with intensity proportional to the percentage.

Bordaigorl
  • 15,135
  • 1
    Really neat, nicely done. – Etheryte Jun 04 '14 at 22:57
  • 15
    If there is enough interest, we could try to write a package for simple maps with a \loadmapof command to load the map's definitions on demand and a command \drawmapof{name}[style] command to produce it with custom shadings and labels... – Bordaigorl Jun 05 '14 at 07:41
  • Good solution!!! –  Jun 05 '14 at 12:33
  • 2
    I would be utterly interested in such a package. Does anyone know if someone continued this work? – Jürgen Apr 27 '15 at 11:18
  • 2
    @Jürgen there is no update since this post but we could start a github project and set it up so that who's interested can contribute. The difficult part is deciding how to organise the data... – Bordaigorl Apr 27 '15 at 14:25
  • 1
    Yeah, there will be some amounts of data, for sure. Perhaps it might be good to separate them into continental files? But furthermore: which data will be necessary or better nice to have? National borders of course, main rivers, capitals and other large cities. Borders within countries (like the states of the US or the states of Germany)? Perhaps it would be reasonable to start with some main countries (maybe on request or based on the numbers of TeXies) because otherwise there may be no start at all. --- Some time ago I tried to translate a SVG map with Inkscape but failed completely ... :-( – Jürgen Apr 28 '15 at 14:28
  • Really creative! And this procedure can be used with every country! Well done! =) –  Mar 05 '17 at 07:53
  • @Bordaigorl, are you still interested in writing the package? – Enlico Mar 05 '17 at 16:41
  • @EnricoMariaDeAngelis I can try to help. I have knowledge of tikz, but not much experience with maps in particular, so I guess the first thing would be to identify some basic use-cases and get them right, then publish and attract contributors – Bordaigorl Mar 06 '17 at 13:13
  • @Bordaigorl, I've never written a LaTeX package so far, so I have honestly no idea where to start from. I wonder what is the most proper way of storing coordinates; I'd be tempted to write a 2 column file -- so that \draw plot file{...} could be used -- with blank lines to separate non connected zones (a country with two islands would have two blank lines). But this implies that the first node should be repeated before the blank line, thus giving up on the smooth joint provided by --cycle. We could address this and other points of the discussion together. – Enlico Mar 19 '17 at 15:36
  • I posted a related question here (a simple answer in comment) – Enlico Mar 19 '17 at 18:19
  • 8
    I created a world version using this map, and you can check it here. – El Andi May 13 '17 at 07:11
16

Not a full solution, but perhaps a way to go on with it.

You have to install, if not done already, all map data files into some place, like pst-geo/data of your tex distribution and add the path to the \WorldMap command.

All data files can be obtained from ctan also: http://www.ctan.org/tex-archive/graphics/pstricks/contrib/pst-geo/data

Running with xelatex works, for some reason latex and consequent conversion with dvipdf fails.

The example is taken from the book of Herbert Voss (Example 25-3-1), available from http://www.ctan.org/tex-archive/info/examples/PSTricks_en

\documentclass{article}

\usepackage{pstricks,pst-map2d}

\begin{document}
\psset{unit=3.7,linewidth=.75pt}
\begin{pspicture*}(-6.5,1)(-3,3)
  \WorldMap[rivers=true,city=true,USA=true,maillage=true,path=/usr/local/texlive/2013/texmf-dist/tex/latex/pst-geo/data]
\end{pspicture*}

\end{document}

enter image description here

  • I assume you need to go through PostScript, i.e. latex -> dvips -> ps2pdf. – Torbjørn T. Jun 04 '14 at 17:11
  • @TorbjørnT. Yes and no: I works only after disabling -dSAFER option of ps2pdf or dvipdf scripts. It is stated in Herbert Voss' book as well, I should have read it before ;-) –  Jun 05 '14 at 12:25
10

I know this thread is a bit stale, but I really liked Bordaigorl's answer above. Yet I am lazy, so I wrote a little perl script that translates the Inkscape-output into a nice macro source, along the lines of Bordaigorl's example above.

Steps to do:

  1. Download the SVG-map from Wikipedia. I used the World Map and also the "compact" version, which is a bit more flat and excludes the polar regions. So that is https://commons.wikimedia.org/wiki/File:BlankMap-World6.svg and https://commons.wikimedia.org/wiki/File:BlankMap-World6,_compact.svg.
    1. Open the SVG-file is Inkscape and export it as Tikz. In the export options, enable "verbose" mode, as this puts in all the object names as comments.
    2. Now path it through the perl script I add below, such as cat Worldmap-raw.tex | ./Map2tikz.pl >Worldmap.tex
    3. Include the file in TeX. An example is below as well. Depending on the Inkscape output, the "ocean" may be caught as a path and would end up with a style option [Map/state, Map/xx], which gives an error in the first TeX-run. In this case, open the Workmap.tex file, find the Map/xx text (somewhere around line 8 usually) and remove the whole "[Map/state, Map/SD]" part – or replace by a nice ocean style if you prefer.
    4. Exhibit happiness!

Here the perl-part:

#!/usr/bin/perl

# First written: Mon 2018-05-07 11:37:16 UTC+0200 (CEST) by Hendrik G. Seliger
# Last changes:  Mon 2018-05-07 14:36:54 UTC+0200 (CEST) by Hendrik G. Seliger

# A script to read a map exported by Inkscape as verbose tikz
# and set proper path names so we can highlight countries/states
# by using their identifier

###
$, = ' ';       # set output field separator
$\ = "\n";      # set output record separator

# While reading the file, we create three variables
# Set_Defaults: contains the command to set defaults for all regions
# Set_Commands: contains the code to set up commands to change each regions style
# Create_Map: The outline of the actual map. This changes the tikzpicture into a scope

$Set_Defaults = "\\tikzset{Map/.cd, state/.style={fill, draw=white, ultra thick}";
$Set_Commands = "\\tikzset{every state/.style={Map/state/.style={#1}}";
$Create_Map = "\\newcommand{\\Map}[1][]{";

# The we use a variable to always catch the current path name = state identifier
# We use "xx" to mark an unset state
$Current_State = "xx";

while ( <STDIN> ) {
#   chomp;  # strip record separator
    # Remember current line, as we will change it further down
    $Current_Line = $_;
    # Get the current state/country identifier
    if ( $Current_Line =~ /% (\w\w)$/) {
        $Current_State = $1;
        # Make it upper case
        $Current_State =~ s/(.)/\u$1/g;
        $Set_Defaults .= ", " . $Current_State . "/.style={}";
        $Set_Commands .= ", " . $Current_State . "/.style={Map/" . $Current_State .  "/.style={#1}}";
    }

    # rewrite path commands
    # \path -> \path[Map/state, Map/XX]
    # checking for "path " with space at end keeps the global/ocean background
    # which is exactly what we want
    if ( $Current_Line =~ /\\path /) {
        # Determine current style
        $Style = "[Map/state, Map/" . $Current_State . "] ";
        $Current_Line =~ s/path /path$Style/g;
    }

    # rewrite start if tikzpicture
    # \begin{tikzpicture}[y=0.80pt, x=0.80pt, yscale=-1.000000, xscale=1.000000, inner sep=0pt, outer sep=0pt] -> [y=0.80pt,x=0.80pt,yscale=-1, inner sep=0pt, outer sep=0pt,#1]
    if ($Current_Line =~ /\\begin{tikzpicture}\[(.*)\](.*)$/ ) {
        $Current_Line = "\\begin{scope}[" . $1 .",#1]" . $2 . "\n";
    }

    if ($Current_Line =~ /\\end{tikzpicture}/ ) {
        $Current_Line =~ s/\\end{tikzpicture}//g;
    }

    $Create_Map .= $Current_Line;
}

$Set_Defaults .= '}';
$Set_Commands .= '}';
$Create_Map .= "\\end{scope}}";

print $Set_Defaults;
print $Set_Commands;
print $Create_Map;

##### END OF MAIN, SUBROUTINES NEXT #####

Beware that the perl script does no error checking, so if you twiddle with the Inkscape output, it's not going to fly. And, it also may not work with other map files, e.g. if the country/state areas are not names properly.

BTW, I check on two-letter-identifiers for states/countries only, as this collects countries with several regions, such as MI on the US map, or France on the world map. The latter is a good example of breakdown at the next level, as all the departments are not names, so just exported by Inkscape as e.g. "path2168".

And a sample TeX-file (I use lualatex, as my normal pdflatex exceeds its memory on the world map):

% !TeX TS-program = lualatex
\documentclass{standalone}
\usepackage{tikz}

\input{Worldmap.tex}

\begin{document}

\begin{tikzpicture}
\Map[every state={draw=white, ultra thick, fill=black!10},DE={fill=blue},US={fill=red}]

\end{tikzpicture}

\end{document}

And per request, a screenshot of the resulting map: Screenshot of resulting map from TeXstudio viewer

Maybe this helps the one or the other.

-1

Have you considered using NCL or its Python equivalent, PyNGL? It can generated vector images and the product is extremely flexible. Not only you can produce maps, but also pre- and post-process the data to put on that map...

https://www.ncl.ucar.edu/ https://www.pyngl.ucar.edu/

The code is open source, of course. Plenty of examples and easy to install. You could copy the code and adapt it to your needs.

Did this help? Marco