Eratosthenes and the Size of Planet Earth
The Ancient Greek Eratosthenes of Cyrene made the first serious attempt at calculating the size of the Earth. He achieved a surprisingly accurate result which I’ll replicate in Python.
Eratosthenes of Cyrene (c 276BC to c 195/194BC) was chief librarian at the legendary Library of Alexandria. When he wasn't busy frowning and tutting at people for making a noise he took time out to calculate the size of the Earth.
He was aware that at the Summer Solstice (the time when the Sun is at its highest point in the sky) the Sun could be seen reflected in the water at the bottom of a deep well in Syene (now called Aswan, and not to be confused with Cyrene), meaning it was exactly overhead. At home in Alexandria, some way north of Syene, the Sun never gets this high so will never be reflected by the water in a well, and will always cast a shadow to the north of any object it shines on.
The diagram below represents a cross-section of a quarter of the Earth, with Syene and Alexandria at their correct latitudes. I have used poles instead of wells, and if you look at the second zoomed-in view you can see that at Alexandria the Sun casts a shadow, specifically one of 7° 12′ (7 degrees 12 minutes, or 1/60 of a degree) so this is the difference between the two latitudes.
7° 12′ happens to be exactly 1/50 of 360°, meaning the distance between Syene and Alexandria is 1/50 of the total circumference of Earth. (He assumed they were on the same longitude which isn't quite true.)
A common unit of distance at the time was the stade, and Eratosthenes knew the distance between Syene and Alexandria was about 5000 stades. He therefore only needed to multiply 5000 by 50 to get the answer he wanted, 250,000 stades. However, Greek stades and Egyptian stades were slightly different. Was this distance 5000 Greek stades or Egyptian stades?
Using Egyptian stades 5,000 is closer to the actual distance than if we use Greek stades, and also gives a more accurate estimate of the size of our planet so this is probably the unit he used. However, I'll do the calculations using both in the code.
The Project
This project consists of the following files:
degree.py
eratosthenes.py
The files can be cloned/downloaded from the Github repository.
Replicating Eratosthenes' methods requires some fiddly calculations with angles, specifically latitudes, so I have shifted these out to a separate class called Degree.
In eratosthenes.py I use the Degree class to implement the calculations described above.
The Degree Class
This the Degree class which lives in degree.py.
class Degree(object):
def __init__(self, degrees = 0, minutes = 0, seconds = 0):
self._seconds = self._seconds_from_dms(degrees, minutes, seconds)
def __str__(self):
dms = self.get()
return f"{dms['degrees']:02d}° {dms['minutes']:02d}′ {dms['seconds']:02d}″"
def set(self, degrees = 0, minutes = 0, seconds = 0):
self._seconds = self._seconds_from_dms(degrees, minutes, seconds)
def get(self):
degrees = self._seconds // 3600
minutes = (self._seconds - (degrees * 3600)) // 60
seconds = self._seconds - (degrees * 3600) - (minutes * 60)
return {"degrees": degrees, "minutes": minutes, "seconds": seconds}
def _seconds_from_dms(self, degrees, minutes, seconds):
return (degrees * 3600) + (minutes * 60) + (seconds)
def fraction_of_circle(self):
return self._seconds / 1_296_000
def set_between(self, deg1, deg2):
if deg1._seconds > deg2._seconds:
self._seconds = deg1._seconds - deg2._seconds
else:
self._seconds = deg2._seconds - deg1._seconds
def to_decimal(self):
degrees = self._seconds // 3600
remaining_seconds = (self._seconds - (degrees * 3600))
decimal_fraction = remaining_seconds / 3600
return degrees + decimal_fraction
__init__
The class stores an angle in seconds (or to be precise arcseconds, 1/60 of an arcminute) and provides a few handy methods. __init__ takes degrees, minutes and seconds (each defaulting to 0) and calculates and stores self._seconds using a separate function, _seconds_from_dms.
__str__
Here we get the value as a dictionary with degrees, minutes and seconds, and print these out formated like 31° 12′ 32″. Note that the minute and second symbols aren't ordinary single and double quotes but I have used the prime and double prime symbols borrowed from mathematical notation.
set
Here we just call _seconds_from_dms, as in __init__. The method isn't used here but might come in handy if you wish to reuse an instance of the class with different values.
get
This method splits self._seconds into degrees, minutes and seconds, returning the result in a dictionary.
We calculate degrees by floor-dividing (ie // which rounds the result down to the nearest integer) self._seconds by 3600. minutes is calculated in a similar way from the remaining seconds, and finally we calculate seconds from whatever is left over.
_seconds_from_dms
This is used in __init__ and set, and combines degrees, minutes and seconds into a single seconds value.
fraction_of_circle
We use this while replicating Eratosthenes' workings, and it calculates the fraction of a circle the current angle represents.
set_between
This method sets the current angle to the difference between 2 other instances of Degree, and is used in this project to calculate the angle between Alexandria and Syene.
to_decimal
Units divided into 60 sub-units are a pain for calculations so this function calculates the angle with decimal fractions, for example 07° 12′ 00″ is converted to 7.2 degrees.
Doing the Calculations
Now we have the Degree class in place we can go ahead and use it to calculate the size of Earth, just as Eratosthenes did over two millenia ago. This is eratosthenes.py.
import degree
def main():
print("------------------------")
print("| codedrome.com |")
print("| Eratosthenes and the |")
print("| Size of Planet Earth |")
print("------------------------\n")
print("Eratosthenes Values\n-------------------\n")
separation_ancient = degree.Degree(degrees = 7, minutes = 12, seconds = 0)
separation_ancient_decimal = separation_ancient.to_decimal()
distance_stades = 5000
stades_per_degree = distance_stades / separation_ancient_decimal
circumference_stades = stades_per_degree * 360
stades_to_km_Greek = circumference_stades * 0.185
stades_to_km_Egyptian = circumference_stades * 0.1575
print(f"Separation {separation_ancient}\n")
print(f"Separation decimal {separation_ancient_decimal}\n")
print(f"Distance in stades {distance_stades}\n")
print(f"Stades per degree {stades_per_degree:.2f}\n")
print(f"Circumference in stades {circumference_stades}\n")
print(f"Stades to kilometres (Greek) {stades_to_km_Greek}\n")
print(f"Stades to kilometres (Egyptian) {stades_to_km_Egyptian}\n")
print("\nModern Values\n-------------\n")
# Latitude of current Bibliotheca Alexandrina
latitude_Alexandria = degree.Degree(degrees = 31, minutes = 12, seconds = 32)
# Latitude of current Aswan
latitude_Syene = degree.Degree(degrees = 24, minutes = 5, seconds = 20)
separation = degree.Degree()
separation.set_between(latitude_Alexandria, latitude_Syene)
fraction_of_circle = separation.fraction_of_circle()
distance_km = 791.7
circumference_km = distance_km / fraction_of_circle
print(f"Latitude of Alexandria {latitude_Alexandria}\n")
print(f"Latitude of Syene {latitude_Syene}\n")
print(f"Separation {separation}\n")
print(f"Fraction of Circle {fraction_of_circle:.4f}\n")
print(f"Distance in km {distance_km} kilometres\n")
print(f"Circumference {circumference_km:.2f} kilometres\n")
if __name__ == "__main__":
main()
I have actually carried out two separate sets of calculations, firstly with the values and units Eratosthenes would have used and secondly using modern values and units.
The first chunk of code carries out the following steps:
Create a Degree object with the angle used by Eratosthenes
Get the decimal equivalent of this value
Initialise a variable for the distance in stades between the two cities
Calculate the number of stades per degree
Calculating the circumference of Earth by multiplying the previous value by 360. (This is apparently how Eratosthenes did it instead of just multiplying the distance by 50.)
Convert Greek stades to kilometres
Convert Egyptian stades to kilometres
These values are then printed.
Next let's carry out the calculation in a slightly different way, and using exclusively modern units and values. These are the steps involved.
Create Degree objects for the latitudes of the two cities
Create a third Degree object, and call its method to get the angle between the two cities
Calculate the separation as the angle of a circle
Hard code the distance between the cities into a variable
Calculate the circumference of Earth using the distance_km and fraction_of_circle variables
Finally we just print the variables.
Let's Try it Out
Now we can run the program with this command.
python3 eratosthenes.py
This is the end result.
The currently accepted value for the circumference of Earth is 40,030km. Eratosthenes' value assuming Greek stades is therefore somewhat off at about 15% too high but still pretty good. His Egyptian stades estimate is far better at about 2% too low.