Workshop on “Creating maps with Stata and thematic applications” by Ben Jann, Hermosillo, October 24–25, 2023
0. Install geoplot
and its dependencies.
ssc install geoplot, replace
ssc install palettes, replace
ssc install colrspace, replace
ssc install moremata, replace
ssc install geo2xy, replace
ssc install fre, replace // not needed by geoplot, but used in my solution below
To view the documentation of command geoplot
, type help geoplot
. To
view the documentation of command geoframe
, type help geoframe
.
1. Convenient light-weight shape files for Mexico are provided by Carlos Efrain
at https://www.efrainmaps.es/english-version/free-downloads/mexico/.
Download and unzip the following files:
Mexico_States.rar
,
Mexico_Cities.rar
,
Mexico_Hydrography.rar
(rivers),
Mexico_Lakes.rar
, and
Mexico_Roads.rar
.
Convert the shape files to Stata format using geoframe convert
(or
spshape2dta
).
I download the files into subfolder source
and unzip them
therein. I then use geoframe convert
to generate Stata format
datasets in the working directory.
. geoframe convert States using source/Mexico_States, replace (translating Mexico_States from directory source/Mexico_States) (importing .shp file) (importing .dbf file) (creating _ID spatial-unit id) (creating _CX coordinate) (creating _CY coordinate) file States_shp.dta created file States.dta created (type geoframe create States to load the data) . geoframe convert Cities using source/Mexico_Cities, replace (translating Mexico_Cities from directory source/Mexico_Cities) (importing .shp file) (importing .dbf file) (creating _ID spatial-unit id) (creating _CX coordinate) (creating _CY coordinate) file Cities_shp.dta created file Cities.dta created (type geoframe create Cities to load the data) . geoframe convert Rivers using source/Mexico_Hydrography, replace (translating Mexico_Hydrography from directory source/Mexico_Hydrography) (importing .shp file) (importing .dbf file) (creating _ID spatial-unit id) (creating _CX coordinate) (creating _CY coordinate) file Rivers_shp.dta created file Rivers.dta created (type geoframe create Rivers to load the data) . geoframe convert Lakes using source/Mexico_Lakes, replace (translating Mexico_Lakes from directory source/Mexico_Lakes) (importing .shp file) (importing .dbf file) (creating _ID spatial-unit id) (creating _CX coordinate) (creating _CY coordinate) file Lakes_shp.dta created file Lakes.dta created (type geoframe create Lakes to load the data) . geoframe convert Roads using source/Mexico_Roads, replace (translating Mexico_Roads from directory source/Mexico_Roads) (importing .shp file) (importing .dbf file) (creating _ID spatial-unit id) (creating _CX coordinate) (creating _CY coordinate) file Roads_shp.dta created file Roads.dta created (type geoframe create Roads to load the data)
2. Load the shape files using geoframe create
.
The data on rivers and lakes I declare as feature type water
.
. geoframe create States, replace (reading shapes from States_shp.dta) (all observations in frame States_shp matched) (link to frame States_shp added) (current frame now States) Frame name: States Frame type: unit Feature type: <none> Number of obs: 32 Unit ID: _ID Coordinates: _CX _CY Area: <none> Linked shape frame: States_shp . geoframe create Cities, replace (reading shapes from Cities_shp.dta) (all observations in frame Cities_shp matched) (link to frame Cities_shp added) (current frame now Cities) Frame name: Cities Frame type: unit Feature type: <none> Number of obs: 36 Unit ID: _ID Coordinates: _CX _CY Area: <none> Linked shape frame: Cities_shp . geoframe create Rivers, replace feature(water) (reading shapes from Rivers_shp.dta) (all observations in frame Rivers_shp matched) (link to frame Rivers_shp added) (current frame now Rivers) Frame name: Rivers Frame type: unit Feature type: water Number of obs: 30 Unit ID: _ID Coordinates: _CX _CY Area: <none> Linked shape frame: Rivers_shp . geoframe create Lakes, replace feature(water) (reading shapes from Lakes_shp.dta) (all observations in frame Lakes_shp matched) (link to frame Lakes_shp added) (current frame now Lakes) Frame name: Lakes Frame type: unit Feature type: water Number of obs: 3 Unit ID: _ID Coordinates: _CX _CY Area: <none> Linked shape frame: Lakes_shp . geoframe create Roads, replace (reading shapes from Roads_shp.dta) (all observations in frame Roads_shp matched) (link to frame Roads_shp added) (current frame now Roads) Frame name: Roads Frame type: unit Feature type: <none> Number of obs: 105 Unit ID: _ID Coordinates: _CX _CY Area: <none> Linked shape frame: Roads_shp
3. Draw a map displaying state boundaries, rivers and lakes.
. geoplot (area States) (area Lakes) (line Rivers)
4. Add markers and labels for cities. Distinguish the style
by variable CAPITAL
.
First have a look at variable CAPITAL
and encode it for use in
geoplot
.
. frame change Cities . tab CAPITAL CAPITAL | Freq. Percent Cum. ------------+----------------------------------- C | 1 2.78 2.78 N | 4 11.11 13.89 Y | 31 86.11 100.00 ------------+----------------------------------- Total | 36 100.00 . label define capital 1 "C" 2 "Y" 3 "N" . encode CAPITAL, generate(capital) label(capital)
Now add the cities to the map. I use automatic styles by providing the city
type as an argument (prefix i.
instructs geoplot
to treat the variable as categorical).
. geoplot (area States) (area Lakes) (line Rivers) ///
> (point Cities i.capital, msymbol(O d T) ///
> label(1 "Country capital" 2 "State capital" 3 "Other", reverse)) ///
> (label Cities NAME i.capital, size(1.75) position(12))
I included the city names as a separate layer. An alternative would be to
use the mlabel()
option in the main layer. The following
example also shows you how to change the colors (see help colorpalette
for available color palettes).
. geoplot (area States) (area Lakes) (line Rivers) ///
> (point Cities i.capital, msymbol(O d T) color(Darjeeling1) ///
> mlabel(NAME) mlabsize(1.75) mlabposition(12) ///
> label(1 "Country capital" 2 "State capital" 3 "Other", reverse))
Instead of assigning styles automatically, we could also add the city types
as separate layers and then collect the legend keys using
legend(layout())
. I also switch to black color for the city
names.
. geoplot (area States) (area Lakes) (line Rivers) ///
> (point Cities if capital==3, msymbol(T) label("Other")) ///
> (point Cities if capital==2, msymbol(d) label("State capital")) ///
> (point Cities if capital==1, msymbol(O) label("Country capital")) ///
> (label Cities NAME, color(black) size(1.75) position(12)) ///
> , legend(layout(6 5 4))
In all of the above graphs there are issues with the positioning of the
labels (e.g. labels printed on top of each other). We fix this
by storing the custom positions as a variable and then provide this
variable to geoplot
.
. generate byte vpos = 12 . replace vpos = 6 if NAME=="Mexicali" (1 real change made) . replace vpos = 1 if NAME=="Monterrey" (1 real change made) . replace vpos = 6 if NAME=="Acapulco" (1 real change made) . replace vpos = 9 if NAME=="Toluca" (1 real change made) . replace vpos = 6 if NAME=="Cuernavaca" (1 real change made) . replace vpos = 4 if NAME=="Puebla" (1 real change made) . replace vpos = 3 if NAME=="Tlaxcala" (1 real change made) . geoplot (area States) (area Lakes) (line Rivers) /// > (point Cities i.capital, msymbol(O d T) color(Darjeeling1) /// > label(1 "Country capital" 2 "State capital" 3 "Other", reverse)) /// > (label Cities NAME, color(black) size(1.75) vposition(vpos))
5. Now expand the plot by including roads. Distinguish style by type of
road. Furthermore, try to put together a composite legend including road and
city type using legend(layout())
.
. frame change Roads . tab TYPE TYPE | Freq. Percent Cum. -------------------+----------------------------------- Multi-Lane Divided | 13 12.38 12.38 Paved Divided | 24 22.86 35.24 Paved Undivided | 68 64.76 100.00 -------------------+----------------------------------- Total | 105 100.00 . encode TYPE, gen(rtype) . geoplot (area States) (area Lakes) (line Rivers) /// > (line Roads i.rtype, color(Salmon) lwidth(.5 .15) label(, reverse)) /// > (point Cities i.capital, msymbol(O d T) color(Darjeeling1) /// > label(1 "Country capital" 2 "State capital" 3 "Other", reverse)) /// > (label Cities NAME, color(black) size(1.75) vposition(vpos)) /// > , legen(layout(- "{bf:Cities}" 5 | - "{bf:Roads}" 4))