Raster Data Scatter Plot Using Python | by Sutan Ashari Mufti | Towards Data Science

Raster data is basically an image or matrix. As an image or matrix, it is consisted of arrays and can be formed as data frames or spreadsheets. You can even visualize raster data in Adobe Photoshop or Ms. Excel! Each raster cell holds a single numeric value that can mean anything as it depends on the context. This value can be elevation, land cover index, NDVI (Normalized Difference vegetation Index) all depending on the context.
In some cases, we might want to see the relationship between rasters. For example, does slope affect greeneries? What is the relationship between NDVI and NDBI? Does equidistance to transportation facility correlate with Nitrogen Dioxide or any other pollution gasses?
Although some software provides a way to display a scatter plot between two raster data, I always find it infuriating. Displaying the plot needs high ram and CPU usage so it often causes lag. The window panes take time to load, changing things or plot properties also takes time to load. Besides, sometimes, we need to automate our works without having to do tedious tasks such as layouts and prettying our graph again and again and again. This becomes worse when we deal with high-resolution rasters which requires even more RAM or CPU!
Fortunately, Python has a way to do such a task without having to do the tedious things. With Python, the need for RAM and CPU capacity can also be reduced when displaying the scatter plot because there is no need for GUI and minimizing the display. I would like to share this process, and thus, This article’s main objective is to show you how to preprocess rasters (using GIS) and display them in a scatter plot using Python.
A Scatter plot needs 2 inputs, and both inputs need to be one dimensional. Raster data, as its nature, is 2-dimensional data, so we need to reshape the data into one dimensional. This reshaping is done by raveling.
Before the raveling, some criteria must be met before we start the raveling process. Each cell of the raster must be ensured to be spatially aligned. This makes sure that one dimensional raveled data is logical and resembles correspondency. If there is a spatial mismatch between 2 raster data, then the scatter plot that is produced will draw false plotting. Furthermore about the criteria will be explained in the Data Criteria section.
Anyway, raveling produces a single-dimensional array with a length of columns times rows. Ensuring both raster data have the same columns and rows means ensuring the length of the raveled data to be precise.
If you want to proceed, it is recommended that you understand
The basis of GIS analysis, to do some raster masking/extracting
Geospatial data Types (raster data), understanding the properties of the raster data
Projection system, to transform raster data’s spatial metrics.
Python. Libraries: NumPy (to deal with the array), matplotlib (plotting and data visualization), and Image Analysis (using PIL (Pillow)). Well, we need the Image analysis part only to read the raster data and convert them to NumPy array, so as long as you can read the image raster data, it checks. I recommend PIL because OpenCV or Matplotlib Imread do not do the job directly as we will deal with one-band images.
If we are going to plot two rasters into a scatter plot, then some of the criteria must be met:
The 2 rasters must have the same dimension. This means that both rasters must have the same number of columns and rows, and pixel size. This criterion is important because correspondence between arrays must be consistent such that each cell has one connection to another cell. When plotting, a Y value will be returned for an X input, and it will be confusing if 2 Y values returned when there is only 1 X input; this is not how functions work, and the scatter plot is a function.
The 2 rasters must be spatially aligned and overlaps. A Scatter plot does not indicate spatial alignment and overlaps, so this criterion must be independently assessed using GIS.
The 2 rasters must have the same coordinate reference. I don’t think this is quite crucial, as long as the number of columns and rows is the same. This criterion should be met primarily because we must match the pixel size and spatial adjacency.
If a cell in a raster has a null value, then the correspondence cell of the other raster should be changed to null. Again, plotting such data is not logical. For example, an X input returns a Null value... How should we plot this? Null is not 0, so it’s better to just drop the value.
If your raster data do not meet the criteria, and often they don’t, some geospatial preprocessing must be conducted. I’m going to explain using ArcGIS toolsets, and for other software such as QGis, or even Python Package such as RasterIO or Geopandas, equivalent tools should exist. Please bear with me...
Suppose we have 2 rasters: X-Axis Raster and Y-Axis Raster. These 2 rasters overlap each other and we want to overview the relationship. We can imagine the x-axis raster as an NDVI raster and the Y-axis raster as an Elevation data aster. Because of the different sources and collection methods, these two rasters have different properties. So, some preprocessing is needed.
choose one reference raster. I prefer the lowest scale raster (the least resolution; the smallest number of columns, and rows). You can see the properties in GIS software, and find the columns, rows, and pixel size of the raster. In this case, my raster reference (I’ll name it X-axis raster) has 203 columns, 210 columns, and 158 (meter) pixel size.
now, for example, the Y-axis raster has different properties such as illustrated below. We must make changes to this Y-axis raster such that it has the same properties with X-axis as our raster reference.
there are 3 main processes.
Project the Y-Axis raster to the same coordinate system as the X-Axis raster. If the Y-Axis raster has the same coordinate reference, then this step is not necessary. This ensures that the pixel size has the same unit. In this case, my Y-Axis raster has a very low cell size, this is because Y-axis has a different projection reference.
Resample the Projected Y-Axis: after projecting, the cell size of the Y-axis raster should have the same metric with X-Axis raster, but they may be in different sizes. Resample aggregates or interpolates Y-Axis value to adjust the Y-Axis resolution such that it is the same resolution with X-Axis. so... resample Y-Axis raster to the resolution of X-Axis raster.
Double extract rasters by masking off each other: double masking ensures the elimination of the null values that overlap with non-null values. So, first, mask Y-Axis by X-axis (returns Masked Y-axis), and then extract X-axis by the masked Y-axis. You now should now have 2 preprocessed rasters that meet the criteria! the masked Y-Axis raster, and the masked X-axis raster.
Finally, export these rasters into TIFF format, with 32-bit float pixel type, and No Data Value as -10 (or any other number! you must note this No Data Value!)
Now, the data are prepared, it’s time to cook. The steps are really simple!
Define the Ravelling Function. reading the raster, cleaning the raster, and raveling the raster. Function declaration shorts the script.
Ravel each of the raster data into 1-dimensional arrays (Using Ravelling Function)
plot each raveled raster!
We are going to need these libraries:
Numpy to do the raveling and managing the arrays
Pillow to read the rasters
Matplotlib to visualize the scatter plot
as I explained in the logic section, we must ravel each raster. We will ravel the raster using fetch_data function below. The fetch_data function reads and cleans the raster data. Cleaning is important as the TIF images contain supposed to be null values, but are not yet null. Cleaning converts these values into nulls to be later be dropped. The fetch_data returns raveled, clean, and ready to plot raster.
After defining the function, we ravel the X-Axis raster and Y-Axis raster using the fetch_data function. Now we have x_data and y_data.
#path to each raster datax_data_path = "folder/x_raster.tif"y_data_path = "folder/y_raster.tif"#raveling the datay_data = fetch_data(y_data_path)X_data = fetch_data(x_data_path)
This part is just matplotlib visualization basic. you can use simple scatter code such as below.
import matplotlib.pyplot as pltplt.rcParams["font.family"] = "Times New Roman"#Declaring the figure, and hiding the ticks' labelsfig, ax = plt.subplots(figsize=(15,8))ax.set_yticklabels([])ax.set_xticklabels([])#Actually Plotting the dataplt.scatter(x_data,y_data, s=0.1, c='black')#Making the graph pretty and informative!plt.title("Raster Data Scatter Plot", fontsize=28)plt.xlabel("X-Axis Raster", fontsize=22)plt.ylabel("Y-Axis Raster", fontsize=22)plt.show()
Using the codes above, we should have this plot result. There you have it! The Scatter plot of two rasters!
The plotting process is quite direct, but the preprocessing using GIS is tricky. In this case, the GIS pre-processing needs human supervision, so you must be fully aware and careful.
The raveling only works if the raster inputs are spatially aligned. This spatial alignment is perhaps the most important aspect that should be thoroughly checked. This steps so crucial because raveling and plotting the data will always yield a scatter plot as long as the dimensions are the same. This illusive scatter plot will mislead because it does not have any indication of spatial alignment yet in essence, it is displaying spatially misaligned information.
For example, a 34x78 dimension 10m resolution raster technically can be plotted with a 78x34 dimension 10m resolution raster. When raveled, they yield the same one-dimensional length, 34x78=2652. You can also technically plot these rasters but they will mean nothing. Even worse if both rasters are not overlapping.
So, always double-check that your raster data are in the same dimension, and spatially aligned. Good luck!