Lesson_03_mapProjection.png

Map projections is a systematic transformation a location on the surface of the earth or a portion of the earth (sphere or an ellipsoid) into on a plane (flat piece of paper or computer screen).

The coordinate reference system (CRS) or spatial reference system (SRS) is a coordinate based system used to locate features on the earth.

Pic0.png

In this section., we will cover following topics:

Data for this lesson could be found here

Geographic coordinate system (GCS)

Geographic coordinate systems use latitude and longitude to measure and locate feature on the globe. The GCS defines a position as a function of direction and distance from a center point of the globe, where the units of measurement are degrees. Any location on earth can be referenced by a point with longitude and latitude coordinates. For example, below figure shows a geographic coordinate system where a location is represented by the coordinate's longitude 80 degree East and latitude 55 degree North.

Pic1.png

The equidistant lines that run east and west each have a constant latitude value called parallels. The equator is the largest circle and divides the earth in half. It is equal in distance from each of the poles, and the value of this latitude line is zero. Locations north of the equator has positive latitudes that range from 0 to +90 degrees, while locations south of the equator have negative latitudes that range from 0 to -90 degrees.

Pic2.png

The lines that run through north and south each have a constant longitude value and form circles of the same size around the earth known as meridians. The prime meridian is the line of longitude that defines the origin (zero degrees) for longitude coordinates. One of the most commonly used prime meridian locations is the line that passes through Greenwich, England. Locations east of the prime meridian up to its antipodal meridian (the continuation of the prime meridian on the other side of the globe) have positive longitudes ranging from 0 to +180 degrees. Locations west of the prime meridian has negative longitudes ranging from 0 to -180 degrees.

Pic3.png

The latitude and longitude lines cover the globe to form a grid that know as a graticule. The point of origin of the graticule is (0,0), where the equator and the prime meridian intersect. The equator is the only place on the graticule where the linear distance corresponding to one degree latitude is approximately equal the distance corresponding to one degree longitude. Because the longitude lines converge at the poles, the distance between two meridians is different at every parallel.

The World Geodetic system 84

The most recent geographic coordinate system is the World Geodetic system 84 also known as WGS 1984 or EPSG:4326 (EPSG- European Petroleum Survey Group). It consists of a standard coordinate system, spheroidal reference (the datum or reference ellipsoid) and raw altitude.

Projected coordinate system

A projected coordinate system provide mechanisms to project maps of the earth's spherical surface onto a two-dimensional Cartesian coordinate (x,y coordinates) plane. Projected coordinate systems are referred to as map projections. This approach is useful where accurate distance, angle, and area measurements are needed. The term 'projection' is often used interchangeably with projected coordinate systems.

Commonly use projected coordinate systems include:

  • Universal Transverse Mercator

  • Albers Equal Area Conic

Universal Transverse Mercator

The most widely used two-dimensional Cartesian coordinate system is the Universal Transverse Mercator (UTM) system which represents a horizontal position on the globe and can be used to identify positions without having to know their vertical location on the 'y' axis. The UTM system is not a single map projection. It represents the earth as sixty different zones, each composed of six-degree longitudinal bands, with a secant transverse Mercator projection in each. The UTM system divides the Earth between 80 S and 84 N latitude into 60 zones, each 6 of longitude in width. Zone 1 covers longitude 180 to 174 W; zone numbering increases eastward to zone 60, which covers longitude 174 to 180.

Pic4.png

The Universal Transverse Mercator grid that covers the conterminous 48 United States comprises 10 zones, from Zone 10 on the west coast through Zone 19 in New England USGS, 2001.The New York State falls in zones 17N (EPSG 26917) and zone 18N (EPSG: 26918) with North American Datum 1983 (NAD83).

Pic5.png

Albers Equal Area Conic

Albers Equal Area Conic projection system uses two standard parallels to reduce the distortion of shape in the region between the standard parallels. This projection is best suited for area extending in an east-to-west orientation like the conterminous United States. Used for the conterminous United States, normally using 29-30 and 45-30 as the two standard parallels. For this projection, the maximum scale distortion for the 48 states of the conterminous United States is 1.25 percent.

Pic6.png

Load Libraries

In [1]:
import os
from glob import glob
import geopandas as gpd
%matplotlib inline
import matplotlib.pyplot as plt
import numpy as np
import geopandas as gpd
import rasterio as rio
from rasterio.plot import show
import rasterio.warp
import rasterio.shutil
from rasterio.warp import calculate_default_transform, reproject, Resampling

Set working directory

In [2]:
path= "C:/Users/zahmed2/Documents/spatial_data_processing_and_analysis_python/Lesson_03_map_pojection_coordinate_reference_system/"
os.chdir(path)
print("Current Working Directory " , os.getcwd())
Current Working Directory  C:\Users\zahmed2\Documents\spatial_data_processing_and_analysis_python\Lesson_03_map_pojection_coordinate_reference_system

Coordinate Reference System in Python

Vector Data

In [3]:
# Import NY Ctate County Shape file 
fp_poly_gcs="Data_03//NY_County_GCS.shp"
poly_gcs= gpd.read_file(fp_poly_gcs)

Check CRS

View the CRS of each layer using .crs attribute of geopandas dataframes (e.g. dataframename.crs).

In [4]:
print(poly_gcs.crs)
{'init': 'epsg:4326'}

We noticed that CRS of NYS county shape has not been be defined yet. So you need to define its current CRS (WGS 1984 or EPSG:4326) before you do any further analyses like re-projection etc. Yoy would set the projection by assigning the WGS84 latitude-longitude CRS (EPSG:4326) to the crs attribute:

In [5]:
poly_gcs.crs = {'init' :'epsg:4326'}
print(poly_gcs.crs)
{'init': 'epsg:4326'}
In [6]:
ax1=poly_gcs.plot()
# add a title
ax1.set_title('WGS84 (lat/lon)')
Out[6]:
Text(0.5, 1, 'WGS84 (lat/lon)')

Re-pojection

We will transform county shape file from WGS 1984 to Albers Equal Area Conic NAD1983 (EPSG:5070). You could reproject the data from one CRS to another CRS using the geopandas method (i.e. function belonging to geopandas objects): to_crs(). Function .to_crs() will only work if your original spatial object has a CRS assigned to it AND if that CRS is the correct CRS for that data!

GeoPandas uses pyproj for the projections (requirements) and pyproj does not know the crs EPSG:5070 (does not exist) nor the ESRI:10203. But you can try the Proj4 string of the ESRI:10203 projection

Polygoan

In [7]:
# Define EPSG:5070 projection
new_crs={'init': 'epsg:5070'}
In [8]:
poly_proj=poly_gcs.to_crs(new_crs)
In [9]:
ax2=poly_proj.plot()
# add a title
ax2.set_title('WGS84 (lat/lon)')
Out[9]:
Text(0.5, 1, 'WGS84 (lat/lon)')
In [10]:
# Make subplots that are next to each other
fig, (ax1, ax2) = plt.subplots(nrows=1, ncols=2, figsize=(12, 8))

# Plot the data in WGS84 CRS
poly_gcs.plot(ax=ax1, facecolor='gray');

# Add title
ax1.set_title("WGS84");

# Plot the one with ETRS-LAEA projection
poly_proj.plot(ax=ax2, facecolor='blue');

# Add title
ax2.set_title("Albers Equal Area Conic");

# Remove empty white space around the plot
plt.tight_layout()

Point shape file

In [11]:
# Load point shape file
fp_point_gcs="Data_03/GP_Point_GCS.shp"
point_gcs= gpd.read_file(fp_point_gcs)
In [12]:
# Define projection
point_gcs.crs = {'init' :'epsg:4326'}
print(point_gcs.crs)
{'init': 'epsg:4326'}
In [13]:
# plot map
ax3=point_gcs.plot()
ax3.set_title('WGS84 (lat/lon)')
Out[13]:
Text(0.5, 1, 'WGS84 (lat/lon)')
In [14]:
# Change to EPSG:5070 projection
point_proj=point_gcs.to_crs(new_crs)
In [15]:
ax4=point_proj.plot()
ax4.set_title('Albers Equal Area Conic)')
Out[15]:
Text(0.5, 1, 'Albers Equal Area Conic)')
In [16]:
## Shave projected file
point_proj.to_file("GP_Point_PROJ.shp")

Raster Data

This exercise you will learn how to projection transformation will be done of raster data. In this exercise, we will use SRTM 90 digital Elevation Model New York State which was downloaded from CGIAR-CSI. We will re-project it from WSG84 coordinate to Albers Equal Area Conic NAD83 projection system.

Projection of a Single Raster

In [17]:
dem_path = "Data_03/Onondaga_DEM.tif"
dem=rio.open(dem_path)
show(dem)
Out[17]:
<matplotlib.axes._subplots.AxesSubplot at 0x14659dffb00>

Read raster file properties

In [18]:
# Check projection
dem.crs
Out[18]:
CRS.from_epsg(4326)
In [19]:
# Check projection with GDAL
import gdal
!gdalinfo "Data_03/Onondaga_DEM.tif"
Driver: GTiff/GeoTIFF
Files: Data_03/Onondaga_DEM.tif
Size is 534, 443
Coordinate System is:
GEOGCS["WGS 84",
    DATUM["WGS_1984",
        SPHEROID["WGS 84",6378137,298.257223563,
            AUTHORITY["EPSG","7030"]],
        AUTHORITY["EPSG","6326"]],
    PRIMEM["Greenwich",0],
    UNIT["degree",0.0174532925199433],
    AUTHORITY["EPSG","4326"]]
Origin = (-76.500125805449841,43.273334046987991)
Pixel Size = (0.001133064032468,-0.001133064032468)
Metadata:
  AREA_OR_POINT=Area
  DataType=Generic
Image Structure Metadata:
  COMPRESSION=LZW
  INTERLEAVE=BAND
Corner Coordinates:
Upper Left  ( -76.5001258,  43.2733340) ( 76d30' 0.45"W, 43d16'24.00"N)
Lower Left  ( -76.5001258,  42.7713867) ( 76d30' 0.45"W, 42d46'16.99"N)
Upper Right ( -75.8950696,  43.2733340) ( 75d53'42.25"W, 43d16'24.00"N)
Lower Right ( -75.8950696,  42.7713867) ( 75d53'42.25"W, 42d46'16.99"N)
Center      ( -76.1975977,  43.0223604) ( 76d11'51.35"W, 43d 1'20.50"N)
Band 1 Block=128x128 Type=Float32, ColorInterp=Gray
  NoData Value=-3.40282306073709653e+38
  Metadata:
    RepresentationType=ATHEMATIC
In [20]:
# All Metadata for the whole raster dataset
dem.meta
Out[20]:
{'driver': 'GTiff',
 'dtype': 'float32',
 'nodata': -3.4028230607370965e+38,
 'width': 534,
 'height': 443,
 'count': 1,
 'crs': CRS.from_epsg(4326),
 'transform': Affine(0.0011330640324679006, 0.0, -76.50012580544984,
        0.0, -0.00113306403246791, 43.27333404698799)}

First, we will re-project one tile of DEM raster data from WGS 1984 CRS to Albers Equal Area Conic projection system. In Python, re-projection of raster little complicated. The code below reprojects between two arrays, using no pre-existing GIS datasets. The rasterio.warp.reproject() has two positional arguments: source and destination. The remaining keyword arguments parameterize the reprojection transform.

Reprojecting a GeoTIFF dataset from one coordinate reference system is a common use case. Rasterio provides a few utilities to make this even easier:

transform_bounds() transforms the bounding coordinates of the source raster to the target coordinate reference system, densifiying points along the edges to account for non-linear transformations of the edges.

calculate_default_transform() transforms bounds to target coordinate system, calculates resolution if not provided, and returns destination transform and dimensions.

In [21]:
inputTIF = 'Data_03/Onondaga_DEM.tif'
projTIF = 'Data_03/Onondaga_DEM_PROJ.tif'

dst_crs = new_crs

with rio.open(inputTIF) as src:
        transform, width, height = calculate_default_transform(
            src.crs, dst_crs, src.width, src.height, *src.bounds)
        kwargs = src.meta.copy()
        kwargs.update({
            'crs': dst_crs,
            'transform': transform,
            'width': width,
            'height': height
        })

        with rio.open(projTIF, 'w', **kwargs) as dst:
            for i in range(1, src.count + 1):
                reproject(
                    source=rio.band(src, i),
                    destination=rio.band(dst, i),
                    src_transform=src.transform,
                    src_crs=src.crs,
                    dst_transform=transform,
                    dst_crs=dst_crs,
                    resampling=Resampling.nearest)
In [22]:
with rio.open(projTIF) as src:
    rasterio.plot.show(src, title='Albers Equal Area Conic', cmap='RdYlGn')

Below reproject_et function wrapps the rasterio reproject code with some addtional arguments that allow to define input path, the output path, and the new CRS components. These three argument only need to change we are going to cahnge project other raster layers. (Source)

In [23]:
def reproject_et(inpath, outpath, new_crs):
    dst_crs = new_crs # CRS for web meractor 

    with rio.open(inpath) as src:
        transform, width, height = calculate_default_transform(
            src.crs, dst_crs, src.width, src.height, *src.bounds)
        kwargs = src.meta.copy()
        kwargs.update({
            'crs': dst_crs,
            'transform': transform,
            'width': width,
            'height': height
        })

        with rio.open(outpath, 'w', **kwargs) as dst:
            for i in range(1, src.count + 1):
                reproject(
                    source=rio.band(src, i),
                    destination=rio.band(dst, i),
                    src_transform=src.transform,
                    src_crs=src.crs,
                    dst_transform=transform,
                    dst_crs=dst_crs,
                    resampling=Resampling.nearest)
In [24]:
reproject_et(inpath = 'Data_03/Onondaga_DEM.tif', 
              outpath = 'Data_03/Onondaga_DEM_PROJ_02.tif', 
              new_crs = "+proj=aea +lat_1=20 +lat_2=60 +lat_0=40 +lon_0=-96 +x_0=0 +y_0=0 +datum=NAD83 +units=m +no_defs")
In [25]:
# Check projection with GDAL
import gdal
!gdalinfo "Data_03/Onondaga_DEM_PROJ_02.tif"
Driver: GTiff/GeoTIFF
Files: Data_03/Onondaga_DEM_PROJ_02.tif
Size is 531, 623
Coordinate System is:
PROJCS["unnamed",
    GEOGCS["NAD83",
        DATUM["North_American_Datum_1983",
            SPHEROID["GRS 1980",6378137,298.2572221010042,
                AUTHORITY["EPSG","7019"]],
            TOWGS84[0,0,0,0,0,0,0],
            AUTHORITY["EPSG","6269"]],
        PRIMEM["Greenwich",0],
        UNIT["degree",0.0174532925199433],
        AUTHORITY["EPSG","4269"]],
    PROJECTION["Albers_Conic_Equal_Area"],
    PARAMETER["standard_parallel_1",20],
    PARAMETER["standard_parallel_2",60],
    PARAMETER["latitude_of_center",40],
    PARAMETER["longitude_of_center",-96],
    PARAMETER["false_easting",0],
    PARAMETER["false_northing",0],
    UNIT["metre",1,
        AUTHORITY["EPSG","9001"]]]
Origin = (1477393.224473777925596,548674.341029610252008)
Pixel Size = (108.512593059673890,-108.512593059673890)
Metadata:
  AREA_OR_POINT=Area
Image Structure Metadata:
  INTERLEAVE=BAND
Corner Coordinates:
Upper Left  ( 1477393.224,  548674.341) ( 76d28'28.07"W, 43d21' 9.43"N)
Lower Left  ( 1477393.224,  481070.996) ( 76d39'15.32"W, 42d47'33.48"N)
Upper Right ( 1535013.411,  548674.341) ( 75d44' 7.87"W, 43d15' 3.83"N)
Lower Right ( 1535013.411,  481070.996) ( 75d55'18.15"W, 42d41'31.15"N)
Center      ( 1506203.318,  514872.668) ( 76d11'47.76"W, 43d 1'21.27"N)
Band 1 Block=531x3 Type=Float32, ColorInterp=Gray
  NoData Value=-3.40282306073709653e+38