I use the thunderforest API to plot points on a map. I figured out how to scale my longitude axis so that it fits the map, using this. Basically I know how width my map is in pixel and I know how many degree of longitude correspond to 256 pixel at each zoom level. With that I figure out how wide my map is in degrees of longitude.
My map is as high as it is wide in pixel (i.e. 1100x1100). So at a zoom level of 14, a width of 1100 pixel corresponds to 0.09453125° longitude. I now assume that a height of 1100 pixel would correspond to 0.09453125° latitude. But this is wrong. As you can see in this map the points should be a circle around the Launch point. But they are in more of an elliptical shape.
I found this on the openstreetmap website.
Values listed in the column "m / pixels" gives the number of meters per pixel at that zoom level for 256-pixel wide tiles. These values for "m / pixel" are calculated with an Earth radius of 6372.7982 km and hold at the Equator; for other latitudes the values must be multiplied by the cosine (approximately assuming a perfect spheric shape of the geoid) of the latitude.
But I couldn't figure what I needed to multiply by cos(latitude). Can someone help me to figure out what to do here?
My full code:
# data
launch_latitude = np.degrees(TOTAL_DATA[0]['TYPE_LATITUDE'][0][0])
launch_longitude = np.degrees(TOTAL_DATA[0]['TYPE_LONGITUDE'][0][0])
# variabels
zoom_level = 14 # 0 <= int <= 20
map_width_pixel = 1100 # max: 2560
save_fig = True
plot_name = 'name'
plot_title = 'NAME'
img_name = f'z{zoom_level}_w{map_width_pixel}_{launch_latitude}N_{launch_longitude}E.png'
img_path = f'../env/images/{img_name}'
tile_width_at_zoom_level = {0:360,1:180,2:90,3:45,4:22.5,5:11.25,6:5.625,7:2.813,8:1.406,9:0.703,10:0.352,11:0.176,12:0.088,13:0.044,14:0.022,15:0.011,16:0.005,17:0.003,18:0.001,19:0.0005,20:0.00025}
map_width_deg = tile_width_at_zoom_level[zoom_level]/256 * map_width_pixel
padding_long = map_width_deg/2
padding_lat = map_width_deg/2
BBox = [launch_longitude - padding_long, launch_longitude + padding_long, launch_latitude - padding_lat, launch_latitude + padding_lat]
# download map, if image doesn't allready exist.
if not os.path.isfile(img_path):
# get URL
zoom = zoom_level
style = 'Landscape'
apikey = '--- API KEY ---'
lon, lat = launch_longitude, launch_latitude
width, height = map_width_pixel, map_width_pixel
URL = f'https://tile.thunderforest.com/static/{style}/{lon},{lat},{zoom}/{width}x{height}.png?apikey={apikey}'
# save img
r = requests.get(URL, stream=True)
r.raw.decode_content = True
with open(img_path,'wb') as f:
shutil.copyfileobj(r.raw, f)
# plot
fig, ax = plt.subplots(figsize = (18,7))
map_img = plt.imread(img_path)
attribution = 'Maps © www.thunderforest.com, Data © www.osm.org/copyright'
ax.imshow(map_img, zorder=0, extent = BBox, aspect= 'equal')
ax.scatter(launch_longitude, launch_latitude, zorder=4, marker='^', color='black', label='Launch')
for zorder, raw_data, label in zip([1,2,3], TOTAL_DATA, ['Nominal Flight', 'No Main', 'No Parachute']):
land_latitude = [np.degrees(flight[-1]) for flight in raw_data['TYPE_LATITUDE']]
land_longitude = [np.degrees(flight[-1]) for flight in raw_data['TYPE_LONGITUDE']]
ax.scatter(land_longitude, land_latitude, zorder=zorder, marker='x', label=label)
ax.set_title(f'{plot_title} Dropzone Analysis')
ax.set_xlim(BBox[0],BBox[1])
ax.set_ylim(BBox[2],BBox[3])
ax.set_xlabel('Longitude [WGS84]')
ax.set_ylabel('Latitude [WGS84]')
ax.text(x=1, y=0.01, s=attribution, horizontalalignment='right', verticalalignment='bottom', rotation='vertical', fontsize=8, color='gray', alpha=0.9, transform=ax.transAxes)
ax.legend()
if save_fig:
plt.savefig(f'../results/{plot_name}_dropzone_analysis.pdf')
plt.show()
Latitude is not scaling the same way as longitude in real life. This has nothing to do with thunderforest or openstreetmap.
When at North Pole, to travel 180⁰ of latitude, you need to walk 20000 kms south (or north, or west, or where ever you want in a straigth line).
To travel 180⁰ of longitude, just turn around. If you happen to be a dervish, you can travel thousand of degrees of longitude in a few seconds. It would take a liftetime to do the same with latitude.
Only at equator (roughly) does latitude and longitude scale the same way.
To be more accurate, since your screenshot shows the latitude, I can tell you that 1100 pixels correspond to 0.09453125°xcos(39.44) = 0.073
degrees of latitude, where you are if they represent 0.09453125° of longitude.
See picture:
Along the "vertical" circles, that is the meridians (those that pass through both poles), 1 degree of latitude (marked by intersction with "horizontal" circles, the parallels) worth always the same distance. Those meridians are just circles split evenly in latitudes sectors.
Those meridians all have the same diameter (which is Earth diameter).
So, 1⁰ along a meridian (along a south-north direction) is always 1/360 the circumference of the Earth.
It is not the same for longitude. Longitude is your position inside one of the "horizontal" circles (the parallels). They are also evenly spaced. So 1⁰ along a parallel, aka 1⁰ of longitude, is also 1/360 the circumference of the circle. But not all those circles, not all parallel share the same diameter, hence the do not share the same circumference.
And if you think at what is the diameter of those circles, that is quite easy. See that flatten image (and pardon my French, litteraly):
You see that the diameter of each parallel is, once projected flat as in the picture, the length of the line. So radius of parallel at latitude 40⁰, is the length of the line marked 40⁰ on this image. And that length is easy to compute. It is the length of the x-axis (aka Equator) times cos(40).
In your case, you are on a parallel at latitude 39.44 degrees. So diameter of the parallel is cos(39.44) times the diameter of the equator. So circumference of the parallel 39.44 is also cos(39.44) the circumference of the Equator. Since circumference of Equator is (roughly) the circumference of the meridians, you can therefore conclude that circumference of parallel at 39.44 is cos(39.44) the circumference of meridian.
So an arc (let of 0.09 degrees) of that parallel is also cos(39.44) the size of an arc of 0.09 degrees of the meridian at the same location.