Climatic variables spatial plotting

Here we are going through step by step on how to spatially place the responses from API requests on a plot using Python

Open you preferable Python IDE or Python console and initially import the required libraries. This example is quite more complex than the previous example Simple climatic variables plotting. Beside the standard libraries also imported in the previous example, we are going to need a couple of geospatial related libraries to better handle coordinates.

import requests
import json
import numpy as np
import geopandas as gpd
import matplotlib.pyplot as plt
import shapely.geometry

After importing the libraries, we define all the needed variables to proceed. Email, password, climatic_variable_1 and coordinates are the essential variables needed to get your responses from the API. The input coordinates of the API are single points, i.e. to time a call is made on the API it refers to specific location requested. Thus to have a collection of spatially distributed points, multiple requests are needed.

It's a good idea to bring all variables that you are going to use up, in order to avoid "hard coding" values later in the code

# Define the user credentials
email = 'YOUR ACCOUNT EMAIL'
password = 'YOUR PASSWORD'
# Define the climatic variable to plot
climatic_variable_1 = 'a-frost-days'  # Average Frost Days
# Define the coordinates of the polygon
lat_point_list = [50.854457, 52.518172, 50.072651, 48.853033, 50.854457]
lon_point_list = [4.377184, 13.407759, 14.435935, 2.349553, 4.377184]
# Create a geopandas structure with the coordinates
polygon_geom = shapely.geometry.Polygon(zip(lon_point_list, lat_point_list))
gdf = gpd.GeoDataFrame(index=[0], crs='epsg:4326', geometry=[polygon_geom])
# total area for the grid
xmin, ymin, xmax, ymax= gdf.total_bounds
# Define the grid
n_cells=5
cell_size = (xmax-xmin)/n_cells
# Projection of the grid
crs = "+proj=sinu +lon_0=0 +x_0=0 +y_0=0 +a=6371007.181 +b=6371007.181 +units=m +no_defs"

So far, the polygon, bounding box and projection are defined. Following a grid is defined within this polygon. This grid contains the point coordinates to be used in the API calls

# Create the cells in a loop
grid_cells = []
for x0 in np.arange(xmin, xmax+cell_size, cell_size):
    for y0 in np.arange(ymin, ymax+cell_size, cell_size):
        # bounds
        x1 = x0-cell_size
        y1 = y0+cell_size
        grid_cells.append(shapely.geometry.box(x0, y0, x1, y1))
cell = gpd.GeoDataFrame(grid_cells, columns=['geometry'], crs=crs)
lng = cell.centroid.x
lat = cell.centroid.y

Following the process, the user needs to generate the temporally authkey to interact with the rest endpoints.

# Step 1: Generate a temporal API key to authentic the other API endpoints
headers = {
    'accept': 'application/json',
    # Already added when you pass json= but not when you pass data=
    # 'Content-Type': 'application/json',
}
json_data = {
    'email': email,
    'password': password,
}
# This the variable that holds the temporally API key
auth_response = requests.post('https://api.answr.space/api:auth/auth/login', headers=headers, json=json_data)

A for loop is used to loop over all points, make the calls to the API and store the responses into a list.

authkey = json.loads(auth_response.content)
for i in range(len(cell)):
    # Step 2: Loop over the points and get the data from the answr.space API
    headers = {
        'accept': 'application/json',
        'Authorization': 'Bearer ' + authkey['authToken'] + '',
    }
    params = {
        'Input_point': '{"type":"point","data":{"lng":' + str(lng[i]) + ',"lat":' + str(lat[i]) + '}}',
    }
    response_1 = requests.get('https://api.answr.space/api:climate-variables/' + climatic_variable_1 + '', params=params,headers=headers)
    # Convert byte responses to json format
    climatic_variable_response = json.loads(response_1.content)
    if response_1.content != b'null':
        # Rename json to key to months for better plot visualization. You can find the response keys in the API reference documentation for each endpoint
        new_key = {"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"}
        old_key = {"CV76_M1", "CV76_M2", "CV76_M3", "CV76_M4", "CV76_M5", "CV76_M6", "CV76_M7", "CV76_M8", "CV76_M9", "CV76_M10", "CV76_M11", "CV76_M12"}
        responses.append(dict(zip(new_key, list(climatic_variable_response.values()))))
    else:
        responses.append(dict(zip(new_key, [0]*12)))

Finally, we can plot the points to spatially understand the variation of the climate statistics.

df_responses = pd.DataFrame(responses)
plt.scatter(lng, lat, c = df_responses['March'], cmap= 'viridis')

In case you want to run the entire process with a single command, bellow you can find the entire code.

import requests
import json
import numpy as np
import geopandas as gpd
import matplotlib.pyplot as plt
import shapely.geometry
import pandas as pd

# Define the user credentials
email = 'YOUR ACCOUNT EMAIL'
password = 'YOUR PASSWORD'
# Define the climatic variable to plot
climatic_variable_1 = 'a-frost-days'  # Average Frost Days
# Define the coordinates of the polygon
lat_point_list = [50.854457, 52.518172, 50.072651, 48.853033, 50.854457]
lon_point_list = [4.377184, 13.407759, 14.435935, 2.349553, 4.377184]
# Create a geopandas structure with the coordinates
polygon_geom = shapely.geometry.Polygon(zip(lon_point_list, lat_point_list))
gdf = gpd.GeoDataFrame(index=[0], crs='epsg:4326', geometry=[polygon_geom])
# total area for the grid
xmin, ymin, xmax, ymax= gdf.total_bounds
# Define the grid
n_cells=5
cell_size = (xmax-xmin)/n_cells
# Projection of the grid
crs = "+proj=sinu +lon_0=0 +x_0=0 +y_0=0 +a=6371007.181 +b=6371007.181 +units=m +no_defs"
# Create the cells in a loop
grid_cells = []
for x0 in np.arange(xmin, xmax+cell_size, cell_size):
    for y0 in np.arange(ymin, ymax+cell_size, cell_size):
        # bounds
        x1 = x0-cell_size
        y1 = y0+cell_size
        grid_cells.append(shapely.geometry.box(x0, y0, x1, y1))
cell = gpd.GeoDataFrame(grid_cells, columns=['geometry'], crs=crs)
lng = cell.centroid.x
lat = cell.centroid.y

# Step 1: Generate a temporal API key to authentic the other API endpoints
headers = {
    'accept': 'application/json',
    # Already added when you pass json= but not when you pass data=
    # 'Content-Type': 'application/json',
}
json_data = {
    'email': email,
    'password': password,
}
# This the variable that holds the temporally API key
auth_response = requests.post('https://api.answr.space/api:auth/auth/login', headers=headers, json=json_data)
responses = []
if auth_response.status_code == 200:
    authkey = json.loads(auth_response.content)
    for i in range(len(cell)):
        # Step 2: Loop over the points and get the data from the answr.space API
        headers = {
            'accept': 'application/json',
            'Authorization': 'Bearer ' + authkey['authToken'] + '',
        }
        params = {
            'Input_point': '{"type":"point","data":{"lng":' + str(lng[i]) + ',"lat":' + str(lat[i]) + '}}',
        }
        response_1 = requests.get('https://api.answr.space/api:climate-variables/' + climatic_variable_1 + '',
                                  params=params,
                                  headers=headers)

        # Convert byte responses to json format
        climatic_variable_response = json.loads(response_1.content)
        if response_1.content != b'null':
            # Rename json to key to months for better plot visualization. You can find the response keys in the API reference documentation for each endpoint
            new_key = {"January", "February", "March", "April", "May", "June", "July", "August", "September", "October",
                       "November", "December"}
            old_key = {"CV76_M1", "CV76_M2", "CV76_M3", "CV76_M4", "CV76_M5", "CV76_M6", "CV76_M7", "CV76_M8",
                       "CV76_M9", "CV76_M10", "CV76_M11", "CV76_M12"}
            responses.append(dict(zip(new_key, list(climatic_variable_response.values()))))

        else:
            responses.append(dict(zip(new_key, [0]*12)))
    # Convert responses to data frame
    df_responses = pd.DataFrame(responses)
    plt.scatter(lng, lat, c = df_responses['March'], cmap= 'viridis')


else:
    print('Return status code: ' + str(auth_response.status_code))
    print(auth_response.content)

Last updated