1

I am trying to calculate all the values contained within a particular radius from a central lat lon position.The code which I am using is as given:

import numpy as np
import matplotlib.pylab as pl
import netCDF4 as nc
import haversine

f = nc.Dataset('air_temp.nc')


def haversine(lon1, lat1, lon2, lat2):
# convert decimal degrees to radians 
lon1 = np.deg2rad(lon1)
lon2 = np.deg2rad(lon2)
lat1 = np.deg2rad(lat1)
lat2 = np.deg2rad(lat2)

# haversine formula 
dlon = lon2 - lon1 
dlat = lat2 - lat1 
a = np.sin(dlat/2)**2 + np.cos(lat1) * np.cos(lat2) * np.sin(dlon/2)**2
c = 2 * np.arcsin(np.sqrt(a)) 
r = 6371
return c * r

# Latitude / longitude grid
#lat = np.linspace(50,54,16)
lat = f.variables['lat'][:]
#lon = np.linspace(6,9,12)
lon = f.variables['lon'][:]
clat = 19.7
clon = 69.7
max_dist = 750      # max distance in km

# Calculate distance between center and all other lat/lon pairs
distance = haversine(lon[:,np.newaxis], lat, clon, clat) 

# Mask distance array where distance > max_dist
distance_m = np.ma.masked_greater(distance, max_dist)

# Dummy data
air = f.variables['air'][0,:,:,:]
data = np.squeeze(air)
data = np.transpose(data)
#data = np.random.random(size=[lon.size, lat.size])
data_m = np.ma.masked_where(distance  >max_dist, data)
# Test: set a value outside the max_dist circle to a large value:
#data[0,0] = 10
#avg = np.nanmean(data_m)-273

I have used the haversine function for finding the distance. Now what I am facing the problem is I need values within a radius of 2.5 degrees from the center point, but I am getting all in kilometers. So if anyone can help me by saying what I am doing wrong or how to it in the right procedure it will be highly appreciated

Debashis Paul
  • 67
  • 1
  • 10
  • 1
    A 'circle' with a radius of 2.5 degrees is not the same shape as a 'circle' with a radius measured in km. The length of a degree varies with position on the Earth's surface. The Haversine formula is specifically for calculating distance in km. If you need distance in degrees you could just use the root of the sum-square of lat and long offsets, although as I said this may give you a very odd shape depending on where you are. – Simon Notley May 17 '20 at 13:05
  • @simonN thnx for going through my code. I am not getting actually which part of the code you are saying, can you please elaborate. – Debashis Paul May 17 '20 at 13:28
  • It's not really a problem with the code itself. You say you want points within 2.5 degrees, but you have code that find points within 750km. Your code just solves a different problem than the one you say you are interested in. You need to replace the function 'haversine' with one that generates the 'distance' in degrees and change your max_dist to 2.5. – Simon Notley May 17 '20 at 13:44
  • Ok got your point.. Will try and see.. Thnx for the comments mate – Debashis Paul May 17 '20 at 14:06
  • @simmon i searched for haversine formula in degrees but not getting any concrete idea. Can you please help me of how to do it if u know. – Debashis Paul May 17 '20 at 14:59

1 Answers1

1

In terms of straight-line (or rather shortest-arc) distance, 1 degree is always 111km (assuming the earth is a perfect sphere (*edited, not "square")).

The center of the shortest arc between any two points on a globe is always the center of the globe. 1 degree = 2π/360 radian, so the distance is R(2π/360) = 6371(2π/360) = 111.19.

Update:

What you missed is not the haversine calculation or the degree-km conversion, it's the understanding of NetCDF's metadata format and NumPy's meshgrid. f.variables['lat'] gives you 37 latitude values and f.variables['lon'] gives you 144 longitude values, so if you want to brute force search all of them, you need to use np.meshgrid to generate a grid of 37*144=5328 points.

Functional code below:

import numpy as np

def haversine(lon1, lat1, lon2, lat2):
    # convert decimal degrees to radians
    lon1 = np.deg2rad(lon1)
    lon2 = np.deg2rad(lon2)
    lat1 = np.deg2rad(lat1)
    lat2 = np.deg2rad(lat2)

    # haversine formula
    dlon = lon2 - lon1
    dlat = lat2 - lat1
    a = np.sin(dlat/2)**2 + np.cos(lat1) * np.cos(lat2) * np.sin(dlon/2)**2
    c = 2 * np.arcsin(np.sqrt(a))
    r = 6371
    return c * r

# center point
ctr_lon, ctr_lat = 69.7, 19.7

# the lon/lat grids
lon = np.arange(0, 360, 2.5)
lat = np.arange(-45, 46, 2.5)

# get coordinates of all points on the grid
grid_lon, grid_lat = np.meshgrid(lon, lat)
dists_in_km = haversine(grid_lon, grid_lat, ctr_lon, ctr_lat)
dists_in_deg = dists_in_km / 111

# find nearby points
thr = 2.5
for i in range(grid_lon.shape[0]):
    for j in range(grid_lon.shape[1]):
        this_lon = grid_lon[i, j]
        this_lat = grid_lat[i, j]
        dist = dists_in_deg[i, j]
        if dist <= thr:
            print('lon=%.1f  lat=%.1f dist=%.2fdeg' % (this_lon, this_lat, dist))

Output:

lon=70.0  lat=17.5 dist=2.22deg
lon=67.5  lat=20.0 dist=2.09deg
lon=70.0  lat=20.0 dist=0.41deg

which makes sense.

xcmkz
  • 677
  • 4
  • 15
  • So which means if i need for 2.5deg i need to multiply 111.19*2.5...is it correct – Debashis Paul May 17 '20 at 15:19
  • but if i do it so their aint any values that are within such distance... All are having higher coordinate values.. Like 8469, 9000 etc – Debashis Paul May 17 '20 at 15:23
  • actually what i want to do is from that central lat lon point i want to get all the values within a radius of 2.5 degree surrounding the central point – Debashis Paul May 17 '20 at 15:26
  • @DebashisPaul IDK what the geolocation metadata of 'air_temp.nc' is but if it's what you indicated in the comments, they do seem to be very far apart... The closest latitude to 19.7 is 16; the closest longitude to 69.7 is 12... – xcmkz May 17 '20 at 15:37
  • the metadata of the data is 2.5*2.5 gridded data with lat, lon, time.. – Debashis Paul May 17 '20 at 15:48
  • @DebashisPaul yes but what's the extent? ie what's the range of `f.variables['lat']` and `f.variables['lon']`? because if they are what you said in the comments they are indeed quite far away from the center point. – xcmkz May 17 '20 at 15:54
  • Lat ranges from - 45 to 45 and lon ranges 0 to 360 – Debashis Paul May 17 '20 at 15:57
  • @DebashisPaul I see - it's not the math's problem, it's the lack of a meshgrid. Please see my updated answer. – xcmkz May 17 '20 at 16:40
  • @xcmkz I'm not sure this is true: "In terms of straight-line (or rather shortest-arc) distance, 1 degree is always 111km (assuming the earth is a perfect square)" Let's assume you meant 'perfect sphere' but in any case 'a degree' would normally mean a degree of latitude or longtude, not a degree along the arc intersecting the two points. I think we need the OP to clarify their problem. – Simon Notley May 17 '20 at 17:23
  • @SimonN Thank you for spotting the typo! Since OP used the haversine formula (which is used to calculate the arc length), I think it's reasonable to assume that the OP meant the angular distance between two points. If the OP meant otherwise, the issue becomes trivial—just calculate the difference in lat and lon. – xcmkz May 17 '20 at 17:40
  • Fair point. To be honest the OPs original code (in km) seems far more likely to be of practical use than either of our suggestions. If a degree is always 111km, why change from a unit the everyone understands to one thats ambiguous. If a degree refers to lat or long it tells you nothing useful about distance so why bother. @Debashis Paul, what do you need this for? – Simon Notley May 17 '20 at 17:57
  • @SimonN i have stated above the purpose which i need, bur i am explaining it again. Like the central point is the coordinates of a storm centre. Now what i intend to do is i want to get all the values that falls within a radius of 2.5 degree from this storm centre and find the average. – Debashis Paul May 17 '20 at 18:21
  • Ok, in that case I'd assume a storm is broadly circular and the same size wherever it occurs therefore @xcmkz's definition of a degree makes most sense. Although I'm not sure why the size of a storm would be definied in degrees and not km. In acny case, you have your answer. – Simon Notley May 17 '20 at 19:27