The previous post looked at how to generate random points on a sphere, generating spherical coordinates directly. I wanted to include a demonstration that this generates points with the same distribution as the more customary way of generating points on a sphere, and then decided the demonstration should be its own post.
I’ll generate random points in Colorado using both methods and show that both put the same proportion of points in the state, within a margin that we’d expect from random points.
I thought about using Wyoming, because I’ve been there recently, or Kansas, because I’m going there soon. Although Wyoming and Kansas are essentially rectangles, there are minor complications to the coordinates of the corners. But Colorado is very simple: it’s bounded by latitudes 37°N and 41°N and by longitudes 102.05°W and 109.05°W.
Here are the two ways of generating points on a sphere.
from numpy import *
# Random point on unit sphere in spherical coordinates
def random_spherical():
rho = 1
theta = 2*pi*random.random()
phi = arccos(1 - 2*random.random())
return (rho, theta, phi)
# Random point on unit sphere in Cartesian coordinates
def random_cartesian():
v = random.normal(size=3)
return v / linalg.norm(v)
We’ll need to convert Cartesian coordinates to spherical coordinates.
def cartesian_to_spherical(v):
rho = linalg.norm(v)
theta = arctan2(v[1], v[0])
phi = arccos(v[2] / rho)
return (rho, theta, phi)
And we’ll need to convert spherical coordinates to latitude and longitude in degrees. Note that latitude is π/2 minus the polar angle.
def spherical_to_lat_long(rho, theta, phi):
lat = rad2deg(pi/2 - phi)
long = rad2deg(theta)
return (lat, long)
And finally, we need a way to test whether a (lat, long) pair is in Colorado.
def in_colorado(lat, long):
return (37 <= lat < 41) and (102.05 <= long <= 109.05)
Now we’ll generate a million random points each way and count how many land in Colorado.
random.seed(20251022)
N = 1_000_000
count = 0
for _ in range(N):
rho, theta, phi = random_spherical()
lat, long = spherical_to_lat_long(rho, theta, phi)
if in_colorado(lat, long):
count += 1
print(count / N)
count = 0
for _ in range(N):
v = random_cartesian()
rho, theta, phi = cartesian_to_spherical(v)
lat, long = spherical_to_lat_long(rho, theta, phi)
if in_colorado(lat, long):
count += 1
print(count / N)
This prints 0.000534 and 0.000536.
Efficiency
Note that this isn’t a very efficient way to generate points in Colorado since only about 5 out of every 10,000 points land in the state. If you wanted to generate points only in Colorado, you could randomly generate latitudes between 37° and 41°N and randomly generate longitudes 102.05° and 109.05°. This might be good enough in practice, depending on the application, but it’s not quite right because Colorado is not a Euclidean rectangle but a spherical rectangle.
A more accurate approach would be to randomly generate longitudes 102.05° and 109.05° as above, but generate latitudes as the arccos of points uniformly generated between cos(37°) and cos(41°).