Curate the cities in Ptolemy's geography and analyze errors​
​by Theodore Moustakas
In some of Ptolemy's work, he provides descriptions of ancient towns and cities of his time. Fortunately for us, he also provided coordinates for each location. Unfortunately, he used a different coordinate system from our own, and the names of the many towns have changed over the centuries. My goal for this project is to associate each ancient town name with a modern location.

Obtaining and Working the Data into a Usable form

The first step I took was to scrape the data off of the cite where Ptolemy’s works were translated and archived.
I first saved the links to each individual chapter as a list, to make importing the data a little easier.
In[]:=
chapters="https://penelope.uchicago.edu/Thayer/E/Gazetteer/Periods/Roman/_Texts/Ptolemy/2/1*.html","https://penelope.uchicago.edu/Thayer/E/Gazetteer/Periods/Roman/_Texts/Ptolemy/2/2*.html",
;
I then iterate over the imported data, taking everything that is a list of three objects , and then going through that list and taking anything that contains a degree sign, essentially making sure that everything taken is a sublist of a name and a pair of coordinates.
Here I use Import and Flatten to get the data into a form that Cases can be used on, and then I use the pattern of three blanks to select the desired cases. I then use Select to take anything from that list that passes the StringContainsQ test on a degree sign:
textTriplets=Cases[Flatten[Import[#,"Data"],1],{_,_,_}]&/@chapters;​​locationsAndCoordinates=Select[Flatten[textTriplets,1],StringContainsQ[StringJoin@#,"°"]&];
I checked what this data looks like by using RandomSample:
In[]:=
RandomSample[locationsAndCoordinates,3]
Out[]=
{{the black stone mountains,63*00,24°40},{mouth of the Himera river,37*15,37°20},{Spoletinum,7*20,38°20}}
I then cut off some poorly formatted data near the end of the list using Part
In[]:=
locationsAndCoordinatesTrimmed=locationsAndCoordinates[[1;;-7]];
I then remove all non-numeric characters from the coordinate pairs
Here I use MapAt to iterate StringReplace over all the coordinates, removing all LetterCharacters, and question marks, which occasionally appeared in the coordinates
In[]:=
locationsAndCoordinatesCleaned=MapAt[StringReplace[#,{LetterCharacter->"","?"->""}]&,locationsAndCoordinatesTrimmed,{All,{2,3}}];

Transforming the Ptolemic Coordinates into our Modern System

One of the main challenges in this project is that Ptolemy used a slightly different coordinate system from our own. He measured the latitudes from the equator, but he measured his longitudes from some point theorized to be in the Canary Isles, because that was the most western explored point at the time. He also miss-estimated the size of the earth.
The first step is to convert the coordinates in the data set from degrees and minutes into decimals of degrees. I also turned the coordinates from strings into expressions in this step.
Here I used MapAt with StringSplit and ToExpression to break all of the coordinates into two pieces and convert them into numbers, so that they are easier to work with mathematically.
In[]:=
locationsWithNumericCoordinates=MapAt[ToExpression,StringSplit[#,{"*","°"}]&/@locationsAndCoordinatesCleaned,{All,{2,3}}];
Then I use a final MapAt in combination with a pure function to transform from degrees and minutes to degrees.
In[]:=
locationsWithDegreeCoordinates=MapAt[#[[1]]+#[[2]]/60.&,locationsWithNumericCoordinates,{All,{2,3}}];
I then pulled the names and coordinates from this list and made them into an association so it would be easier to work with. I also reversed the order of the coordinate pairs, as they were listed as longitude/latitude pairs, rather than latitude/longitude pairs.
In[]:=
locationNames=Flatten[First/@locationsWithDegreeCoordinates,1];​​coordinatesLatLong=Reverse/@locationsWithDegreeCoordinates[[All,{2,3}]];
This next piece of code applies the transformation and assigns the results to a new variable
The reasons behind each specific transformation are fascinating. Ptolemy used a point in the Canary Islands as his Prime Meridian, as it was the western most explored point at the time, and so I had so subtract 17 to account for the difference in origins. I had to divide by 1.428 because Ptolemy got the size of the earth wrong by a factor of exactly 1.428
In[]:=
transformedCoordinates=MapAt
#-17
1.428
&,coordinatesLatLong,{All,2};
This just uses AssociationThread to make an association of locationNames and transformedCoordinates
In[]:=
namesAndTransformedCoordinates=AssociationThread[locationNames,transformedCoordinates];

Narrowing Down the Workable Data

After getting all of the data into a workable form, and transforming the coordinates properly, I now need to weed out any irrelevant data points. Ptolemy marked many points that were simple landmarks, and are not particularly important points. The simplest way to select points of interest was to use Wikipedia.
Here I select from location names only the elements that have a Wikipedia entry.
I used Select in combination with StringQ and WikipediaData to take anything that has a Wikipedia entry
In[]:=
notableLocations=Select[locationNames,StringQ@WikipediaData@#&];
I then create an association containing only the relevant data using MapThread and Rule
In[]:=
notableLocationsAssoc=Association@MapThread[Rule,{notableLocations,namesandshiftedcoords[#]&/@notableLocations}];
And this is a plot of the results:
In[]:=
GeoListPlot[{GeoPosition[{notableLocationsAssoc[#][[1]],notableLocationsAssoc[#][[2]]}]&/@notableLocations},GeoGridLinesAutomatic]
Out[]=

Managing and Calculating Error

Upon closer inspection of the data, using reference points such as Rome, it is very obvious that there is still some significant error. The general trend in the data was that the latitudes tended to be more accurate, and the longitudes were often off by an average of 3 degrees. This made it extremely difficult to achieve my original goal of matching an ancient city name with a modern location, as over the centuries the town names have changed drastically, and so there was no good way to be sure of a match between an ancient location and a modern city. Therefore my goal shifted from trying to match the locations in Ptolemy's geography to modern cities, to analyzing the error in Ptolemy's work.
To measure the error, I first needed towns that had modern day equivalents. I went through my list of cities, and found a number of evenly distributed points that had modern day equivalents. Then, for each point, I calculated the difference in the longitude provided by the transformation of the Ptolemic coordinates and the actual longitude. I then calculated the longitudinal distance between each town and Alexandria, where Ptolemy lived.
The code I used to plot the manually calculated points:
In[]:=
ListPlot[{{33.7,2.8},{32.1,4.08},{33,7.69},{30,1.48},{29.7,3.12},{20,0.27},{11,0.9},{1.5,0},{11,13},{2,1},{17,0.7},{37.5,3},{31.7,5},{36,2.8},{32,2},{29.7,1.45},{29.8,2.4},{2.5,0.6}},AxesLabel->{"Longitudal distance from Alexandria","Longitudal Error (in degrees)"}]
Out[]=
As we can see, there is a clear positive correlation between the distance from Alexandria, and the size of the longitudinal error made by Ptolemy

Conclusion

In conclusion, I was able to transform the Ptolemic coordinates accurately, however there was enough error in the data to make pinpointing a city very difficult. Some reason for this error might be that instead of actually measuring distance, Ptolemy based his coordinates on travel time to any of the given location.
However, there were enough points that had historical significance that I was able to graph the longitudinal error, which displayed an interesting correlation.

Future work ideas:

A couple ideas I have for future work on this project are finding a line of best fit for the graph of error, and then using the function generated to try to modify each coordinate pair to be more accurate, or at least provide a better estimate of an actual location.