Search code examples
python-3.xgiscartopymetpy

Resample list of lat lon points


The code below reads in data about the location of valid ASOS (weather observing stations) around the globe. I'd like to use the list in the future as a list of points to plot data from, but a lot of the stations are way too close together to view at a national or even state-level scale. I'd like to reduce the density of the stations that are plotted on the map. Below is some code that plots all the stations in the US/southern Canada:

import re

fh = open('../498/stations.txt', 'r')
lines = fh.readlines()

data = []
for line in lines:
    comment_match = re.search('^!', line)
    blank_match = re.search('^\s*$', line)
    header_match = re.search('\d{2}-\w{3}-\d{2}|CD\s+STATION', line)
    if comment_match or blank_match or header_match:
        None
    else:
        ICAO = line[20:24].strip()
        if len(ICAO) == 4:
            CD = line[0:3].strip()
            if len(CD) == 0:
                CD = None
            STATION = line[3:20].strip()
            LATLON = line[39:54]
            if LATLON[5] == 'S':
                LAT = float("{0:.2f}".format(-(float(LATLON[0:2])+float(LATLON[3:5])/60.)))
            if LATLON[5] == 'N':
                LAT = float("{0:.2f}".format((float(LATLON[0:2])+float(LATLON[3:5])/60.)))
            if LATLON[14] == 'W':
                LON = float("{0:.2f}".format(-(float(LATLON[8:11])+float(LATLON[12:14])/60.)))
            if LATLON[14] == 'E':
                LON = float("{0:.2f}".format((float(LATLON[8:11])+float(LATLON[12:14])/60.)))
            ELEV = int(line[54:59].strip())
            C = line[81:-1]
            stn_dict = {'name':STATION, 'id':ICAO, 'state':CD, 'country':C, 'lat':LAT, 'lon':LON, 'elev':ELEV}
            data.append(stn_dict)

import cartopy.crs as ccrs
import cartopy.feature as cfeature
import matplotlib.pyplot as plt

extent = [-130,-60,20,60]

fig = plt.figure(figsize=(15,12))
ax = fig.add_subplot(111,projection=ccrs.Miller())
ax.coastlines(resolution='50m')
ax.add_feature(cfeature.STATES.with_scale('50m'))
ax.set_extent(extent,crs=ccrs.Miller())

for stndict in data:
    if stndict['lon'] > extent[0] and stndict['lon'] < extent[1] and stndict['lat'] > extent[2] and stndict['lat'] < extent[3]:
        plt.plot(stndict['lon'], stndict['lat'], color='blue', marker='o',
         transform=ccrs.PlateCarree())

Here is the output of the code. You can see that many plot on top of one another at this map scale. Ideally, I would like to have functionality to change the distance between points plotted, say 100km for instance.

Are there any libraries that would make this task easier?


Solution

  • MetPy has a function reduce_point_density that can do what you want. This function takes your station locations as a (number of points) x dimensions (e.g. 2 or 3) array, along with a radius, the minimum distance to the nearest point in the result. The result is a boolean array that you can use as a mask to select the points of interest. You use it like:

    from metpy.calc import reduce_point_density
    import numpy as np
    point_locs = np.array([(10, 50), (11, 49), (35, 40)])
    mask = reduce_point_density(point_locs, 4.)
    keep_points = point_locs[mask]
    

    You can also optionally pass an array of priority values, which allows controlling which points are preferentially selected to be kept. An bigger example using reduce_point_density is here.