Intro to Coordinate Reference Systems & Spatial Projections
Overview
Teaching: 0 min
Exercises: 0 minQuestions
What are Coordinate Reference Systems?
Objectives
Learn about the key spatial attributes that are needed to work with spatial data including: Coordinate Reference Systems (CRS), Extent and spatial resolution.
After completing this activity, you will understand:
- The basic concept of what a Coordinate Reference System (CRS) is and how it impacts data processing, analysis and visualization.
- Understand the basic differences between a geographic and a projected CRS.
- Become familiar with the Universal Trans Mercator (UTM) and Geographic (WGS84) CRSs
This lesson is a copy of the Data Carpentry lesson Introduction to Coordinate reference systems.
Minor changes have been done and python codes have been added for this tutorial (the original lesson had examples in R
).
Getting Started with CRS
Check out this short video highlighting how map projections can make continents look proportionally larger or smaller than they actually are!
- For more on types of projections, visit ESRI’s ArcGIS reference on projection types..
- Read more about choosing a projection/datum.
What is a Coordinate Reference System
To define the location of something we often use a coordinate system. This system consists of an X and a Y value, located within a 2 (or more) -dimensional space. We live on a 3-dimensional earth that happens to be “round”. To define the location of objects on the earth, which is round, we need a coordinate system that adapts to the Earth’s shape. When we make maps on paper or on a flat computer screen, we move from a 3-Dimensional space (the globe) to a 2-Dimensional space (our computer screens or a piece of paper). The components of the CRS define how the “flattening” of data that exists in a 3-D globe space. The CRS also defines the the coordinate system itself.
A coordinate reference system (CRS) is a coordinate-based local, regional or global system used to locate geographical entities. – Wikipedia {; .callout}
The Components of a CRS
The coordinate reference system is made up of several key components:
- Coordinate System: the X, Y grid upon which our data is overlayed and how we define where a point is located in space.
- Horizontal and vertical units: The units used to define the grid along the x, y (and z) axis.
- Datum: A modeled version of the shape of the earth which defines the origin used to place the coordinate system in space. We will explain this further, below.
- Projection Information: the mathematical equation used to flatten objects that are on a round surface (e.g. the earth) so we can view them on a flat surface (e.g. our computer screens or a paper map).
Why CRS is Important
It is important to understand the coordinate system that your data uses - particularly if you are working with different data stored in different coordinate systems. If you have data from the same location that are stored in different coordinate reference systems, they will not line up in any GIS or other program unless you have a program like ArcGIS or QGIS that supports projection on the fly. Even if you work in a tool that supports projection on the fly, you will want to all of your data in the same projection for performing analysis and processing tasks.
Data Tip: Spatialreference.org provides an excellent online library of CRS information.
Coordinate System & Units
We can define a spatial location, such as a plot location, using an x- and a y-value - similar to our cartesian coordinate system displayed in the figure, above.
For example, the map below, generated in python
with matplotlib
shows all of the
continents in the world, in a Geographic
Coordinate Reference System. The
units are Degrees and the coordinate system itself is latitude and
longitude with the origin
being the location where the equator meets
the central meridian on the globe (0,0).
%matplotlib inline from mpl_toolkits.basemap import Basemap import numpy as np import matplotlib.pyplot as plt # llcrnrlat,llcrnrlon,urcrnrlat,urcrnrlon # are the lat/lon values of the lower left and upper right corners # of the map. # resolution = 'c' means use crude resolution coastlines. map = Basemap(projection='cyl',llcrnrlat=-90,urcrnrlat=90,\ llcrnrlon=-180,urcrnrlon=180,resolution='c') map.drawcoastlines() map.fillcontinents(color='black',lake_color='aqua') # draw parallels and meridians. map.drawparallels(np.arange(-90.,91.,30.)) map.drawmeridians(np.arange(-180.,181.,60.)) map.drawmapboundary(fill_color='aqua') plt.title("Global Map: Equidistant Cylindrical Projection \n Units: Degrees - Latitudes/Longitudes") plt.xticks([-180,-100,0,100,180]) plt.yticks([-90,-50,0,50,90]) plt.xlabel('Longitude (Degrees)') plt.ylabel('Latitude (Degrees)') plt.show()
We can add three coordinate locations to our map. Note that the UNITS are in decimal degrees (latitude, longitude):
- Boulder, Colorado: 40.0274, -105.2519
- Oslo, Norway: 59.9500, 10.7500
- Mallorca, Spain: 39.6167, 2.9833
Let’s create a second map with the locations overlayed on top of the continental boundary layer.
%matplotlib inline from mpl_toolkits.basemap import Basemap import numpy as np import matplotlib.pyplot as plt # llcrnrlat,llcrnrlon,urcrnrlat,urcrnrlon # are the lat/lon values of the lower left and upper right corners # of the map. # resolution = 'c' means use crude resolution coastlines. map = Basemap(projection='cyl',llcrnrlat=-90,urcrnrlat=90,\ llcrnrlon=-180,urcrnrlon=180,resolution='c') map.drawcoastlines() map.fillcontinents(color='black',lake_color='aqua', zorder = 1) # draw parallels and meridians. map.drawparallels(np.arange(-90.,91.,30.)) map.drawmeridians(np.arange(-180.,181.,60.)) map.drawmapboundary(fill_color='aqua', zorder=0) # Add three coordinate locations to our map # Boulder, Colorado: 40.0274, -105.2519 # Oslo, Norway: 59.9500, 10.7500 # Mallorca, Spain: 39.6167, 2.9833 lats = [40.0274, 59.9500, 39.6167] lons = [-105.2519,10.7500,2.9833] plt.scatter(lons,lats,s=100, c='red', zorder=2) plt.title("Global Map: Equidistant Cylindrical Projection \n Units: Degrees - Latitudes/Longitudes") plt.xticks([-180,-100,0,100,180]) plt.yticks([-90,-50,0,50,90]) plt.xlabel('Longitude (Degrees)') plt.ylabel('Latitude (Degrees)') plt.show()
Geographic CRS - The Good & The Less Good
Geographic coordinate systems in decimal degrees are helpful when we need to
locate places on the Earth. However, latitude and longitude
locations are not located using uniform measurement units. Thus, geographic
CRSs are not ideal for measuring distance. This is why other projected CRS
have been developed.
Projected CRS - Robinson
We can view the same data above, in another CRS - Robinson
. Robinson
is a
projected CRS
. Notice that the country boundaries on the map - have a
different shape compared to the map that we created above in the CRS
:
Geographic lat/long WGS84.
%matplotlib inline from mpl_toolkits.basemap import Basemap import numpy as np import matplotlib.pyplot as plt # lon_0 is central longitude of projection. # resolution = 'c' means use crude resolution coastlines. map = Basemap(projection='robin',lon_0=0,resolution='c') map.drawcoastlines() map.fillcontinents(color='black',lake_color='aqua', zorder=1) map.drawparallels(np.arange(-90.,120.,30.),labels=[1,0,0,0]) # draw parallels map.drawmeridians(np.arange(0.,420.,60.),labels=[0,0,0,1]) # draw meridians map.drawmapboundary(fill_color='aqua',zorder=0) plt.title("Global Map: Robinson Projection \n Units: Degrees - Latitudes/Longitudes") plt.show()
Now what happens if you try to add the same Lat / Long coordinate locations that
we used above, to our map, with the CRS
of Robinsons
?
%matplotlib inline import matplotlib.pyplot as plt # lon_0 is central longitude of projection. # resolution = 'c' means use crude resolution coastlines. map = Basemap(projection='robin',lon_0=0,resolution='c') map.drawcoastlines() map.fillcontinents(color='black',lake_color='aqua', zorder=1) # draw parallels and meridians. map.drawparallels(np.arange(-90.,120.,30.),labels=[1,0,0,0]) # draw parallels map.drawmeridians(np.arange(0.,420.,60.),labels=[0,0,0,1]) # draw meridians map.drawmapboundary(fill_color='aqua',zorder=0) # Add three coordinate locations to our map # Boulder, Colorado: 40.0274, -105.2519 # Oslo, Norway: 59.9500, 10.7500 # Mallorca, Spain: 39.6167, 2.9833 lats = [40.0274, 59.9500, 39.6167] lons = [-105.2519,10.7500,2.9833] plt.scatter(lons,lats,s=100, c='red', zorder=2) plt.title("Global Map: Robinson Projection") plt.show()
Notice above that when we try to add lat/long coordinates in degrees, to a map
in a different CRS
, that the points are not in the correct location. We need
to first convert the points to the new projection - a process often referred
to as reprojection.
%matplotlib inline from mpl_toolkits.basemap import Basemap import numpy as np import matplotlib.pyplot as plt # lon_0 is central longitude of projection. # resolution = 'c' means use crude resolution coastlines. map = Basemap(projection='robin',lon_0=0,resolution='c') map.drawcoastlines() map.fillcontinents(color='black',lake_color='aqua', zorder=1) # draw parallels and meridians. map.drawparallels(np.arange(-90.,120.,30.),labels=[1,0,0,0]) # draw parallels map.drawmeridians(np.arange(0.,420.,60.),labels=[0,0,0,1]) # draw meridians map.drawmapboundary(fill_color='aqua',zorder=0) # Add three coordinate locations to our map # Boulder, Colorado: 40.0274, -105.2519 # Oslo, Norway: 59.9500, 10.7500 # Mallorca, Spain: 39.6167, 2.9833 lats = [40.0274, 59.9500, 39.6167] lons = [-105.2519,10.7500,2.9833] # notice the coordinate system in the Robinson projection (CRS) is DIFFERENT # from the coordinate values for the same locations in a geographic CRS. # Reprojection is very simple in python: x,y = map(lons,lats) print(x) print(y) plt.scatter(x,y,s=100, c='red', zorder=2) plt.title("Global Map: Robinson Projection) plt.show()
Compare Maps
Both of the plots above look visually different and also use a different coordinate system. Let’s look at both, side by side, with the actual graticules or latitude and longitude lines rendered on the map.
NOTE: The code for this map can be found in the .py document that is available for download at the bottom of this page!
%matplotlib inline from mpl_toolkits.basemap import Basemap import numpy as np import matplotlib.pyplot as plt # get two plots in the same figure: 2 lines and 1 row fig, axes = plt.subplots(2, 1) ########################################################## # Subplot -1 ########################################################## axes[0].set_title("Equidistant Cylindrical Projection (long/lat Degrees)") # llcrnrlat,llcrnrlon,urcrnrlat,urcrnrlon # are the lat/lon values of the lower left and upper right corners # of the map. # resolution = 'c' means use crude resolution coastlines. map = Basemap(projection='cyl',llcrnrlat=-90,urcrnrlat=90,\ llcrnrlon=-180,urcrnrlon=180,resolution='c', ax=axes[0]) map.drawcoastlines() map.fillcontinents(color='black',lake_color='aqua', zorder = 1) # draw parallels and meridians. map.drawparallels(np.arange(-90.,91.,10.)) map.drawmeridians(np.arange(-180.,181.,10.)) map.drawmapboundary(fill_color='aqua', zorder=0) # Add three coordinate locations to our map # Boulder, Colorado: 40.0274, -105.2519 # Oslo, Norway: 59.9500, 10.7500 # Mallorca, Spain: 39.6167, 2.9833 lats = [40.0274, 59.9500, 39.6167] lons = [-105.2519,10.7500,2.9833] axes[0].scatter(lons,lats,s=100, c='red', zorder=2) ########################################################### # Subplot-2 ########################################################### axes[1].set_title("Robinson projection (meters)") # lon_0 is central longitude of projection. # resolution = 'c' means use crude resolution coastlines. map = Basemap(projection='robin',lon_0=0,resolution='c', ax=axes[1]) map.drawcoastlines() map.fillcontinents(color='black',lake_color='aqua', zorder=1) # draw parallels and meridians. map.drawparallels(np.arange(-90.,120.,10.)) map.drawmeridians(np.arange(0.,360.,10.)) map.drawmapboundary(fill_color='aqua',zorder=0) # Add three coordinate locations to our map # Boulder, Colorado: 40.0274, -105.2519 # Oslo, Norway: 59.9500, 10.7500 # Mallorca, Spain: 39.6167, 2.9833 lats = [40.0274, 59.9500, 39.6167] lons = [-105.2519,10.7500,2.9833] x,y = map(lons,lats) axes[1].scatter(x,y,s=100, c='red', zorder=2) plt.show()
Why Multiple CRS?
You may be wondering, why bother with different CRSs if it makes our
analysis more complicated? Well, each CRS
is optimized to best represent:
- Shape and/or
- Scale / distance and/or
- Area
of features in the data. And no one CRS is great at optimizing shape, distance AND
area. Some CRSs are optimized for shape, some distance, some area. Some
CRSs are also optimized for particular regions -
for instance the United States, or Europe or Nordic countries. Discussing CRS
as it optimizes shape,
distance and area is beyond the scope of this tutorial, but it’s important to
understand that the CRS
that you chose for your data, will impact working with
the data!
Challenge
Compare the maps of the globe above. What do you notice about the shape of the various countries. Are there any signs of distortion in certain areas on either map? Which one is better?
Look at the image below - which depicts maps of the United States in 4 different
CRS
s. What visual differences do you notice in each map? Look up each projection online, what elements (shape,area or distance) does each projection used in the graphic below optimize?
Geographic vs Projected CRS
The above maps provide examples of the two main types of coordinate systems:
- Geographic coordinate systems: coordinate systems that span the entire globe (e.g. latitude / longitude).
- Projected coordinate Systems: coordinate systems that are localized to minimize visual distortion in a particular region (e.g. Robinson, UTM, State Plane)
All the projections available in matplotlib can be found here.
Key Points
Coordinate Reference Systems