There are different ways to map point data to a smooth field. One way is to triangulate the data, smooth it and interpolate to a regular mesh (see previous notebooks). It is also possible to construct weighted averages from scattered points to a regular mesh. In this notebook we work through how to find where points lie in the mesh and map their values to nearby vertices.
The next example is Ex7-Refinement-of-Triangulations
Define a relatively smooth function that we can interpolate from the coarse mesh to the fine mesh and analyse. At local scales it is convenient to use projected coordinate reference systems (CRS) to work in metres instead of degrees. We use the heat flow database for Southeastern Australia from Mather et al. 2017
In [ ]:
import numpy as np
HFdata = np.loadtxt("../Data/HeatFlowSEAustralia.csv", delimiter=',', usecols=(3,4,5), skiprows=1)
eastings = HFdata[:,0]
northings = HFdata[:,1]
heat_flow = HFdata[:,2]
In [ ]:
%matplotlib inline
import matplotlib.pyplot as plt
import cartopy
import cartopy.crs as ccrs
# local coordinate reference system
proj = ccrs.epsg(28354)
fig = plt.figure(figsize=(10,5))
ax = fig.add_subplot(111, projection=ccrs.PlateCarree())
ax.coastlines(resolution='10m')
ax.set_extent([135, 148, -39, -30])
ax.scatter(eastings, northings,
marker="o", cmap=plt.cm.RdBu, c=heat_flow, transform=proj)
ax.gridlines(draw_labels=True)
plt.show()
In [ ]:
import stripy as stripy
xmin = eastings.min()
xmax = eastings.max()
ymin = northings.min()
ymax = northings.max()
extent = [xmin, xmax, ymin, ymax]
# define a mesh with 20km x 20km resolution
spacingX = 10000.0
spacingY = 10000.0
mesh = stripy.cartesian_meshes.square_mesh(extent, spacingX, spacingY, refinement_levels=0, tree=True)
print("number of points = {}".format(mesh.npoints))
In [ ]:
triangles = mesh.containing_triangle(eastings, northings)
tris, counts = np.unique(triangles, return_counts=True)
print("number of unique triangles = {}".format(tris.shape[0]))
In [ ]:
## map to nodes so we can plot this
hit_count = np.zeros(mesh.npoints)
for i in range(0, tris.shape[0]):
hit_count[mesh.simplices[tris[i]]] += counts[i]
hit_count /= 3.0
print("mean number of hits = {}".format(hit_count.mean()))
In [ ]:
fig = plt.figure(figsize=(10,5))
ax = fig.add_subplot(111, projection=ccrs.PlateCarree())
ax.coastlines(resolution='10m')
ax.set_extent([135, 148, -39, -30])
ax.scatter(mesh.x, mesh.y,
marker="o", cmap=plt.cm.Reds, s=100, c=hit_count, alpha=0.33, transform=proj)
ax.gridlines(draw_labels=True)
plt.show()
In [ ]:
distances, vertices = mesh.nearest_vertices(eastings, northings, k=1)
nodes, ncounts = np.unique(vertices, return_counts=True)
hit_countn = np.zeros(mesh.npoints)
hit_countn[nodes] = ncounts
In [ ]:
fig = plt.figure(figsize=(10,5))
ax = fig.add_subplot(111, projection=ccrs.PlateCarree())
ax.coastlines(resolution='10m')
ax.set_extent([135, 148, -39, -30])
ax.scatter(mesh.x, mesh.y,
marker="o", cmap=plt.cm.Reds, s=100, c=hit_countn, alpha=0.33, transform=proj)
ax.gridlines(draw_labels=True)
plt.show()
The k-d tree method provides a specified number of neighbours and the distance to those neighbours. This can be used in a number of ways to smooth or amalgamate data. Here for example is a weighted average of each earthquake to nearby nodes.
We compute the distances to $N$ nearby vertices and distribute information to those vertices in inverse proportion to their distance.
$$ w _i = \frac{d _i}{\sum_{i=1}^N d _i} $$Alternatively, we might map information to the vertices by applying a radially symmetric kernel to the point data without normalising.
In [ ]:
distances, vertices = mesh.nearest_vertices(eastings, northings, k=100)
norm = distances.sum(axis=1)
# distances, vertices are arrays of shape (data_size, 10)
hit_countid = np.zeros(mesh.npoints)
## numpy shouldn't try to vectorise this reduction operation
for i in range(0,distances.shape[0]):
hit_countid[vertices[i,:]] += distances[i,:] / norm[i]
hit_countidr = np.zeros(mesh.npoints)
## numpy shouldn't try to vectorise this reduction operation
for i in range(0,distances.shape[0]):
hit_countidr[vertices[i,:]] += np.exp( -distances[i,:] / 0.02 )
In [ ]:
fig = plt.figure(figsize=(10,5))
ax = fig.add_subplot(111, projection=ccrs.PlateCarree())
ax.coastlines(resolution='10m')
ax.set_extent([135, 148, -39, -30])
ax.scatter(mesh.x, mesh.y,
marker="o", cmap=plt.cm.Reds, s=100, c=hit_countid, alpha=0.33, transform=proj)
ax.gridlines(draw_labels=True)
plt.show()
The next example is Ex7-Refinement-of-Triangulations