The points plotted on the final map (especially the Mercator projection) do not match the continental boundaries. This misalignment suggests that there might be an error in how I'm transforming the coordinates.
I'm using Python to plot occurrences of a species on a world map, but the points do not align correctly with the continents. The data is fetched using the pygbif
library, decoded with mapbox_vector_tile
x and y coords between 0...512.
And then visualized using matplotlib
and cartopy
. However, the plotted points appear misaligned with the continental boundaries.
from pygbif import maps
import mapbox_vector_tile
import matplotlib.pyplot as plt
import cartopy.crs as ccrs
import pandas as pd
import cartopy.feature as cfeature
x = maps.map(
taxonKey = 3034824,
srs='EPSG:3857', # Web Mercator
format = ".mvt"
)
content = mapbox_vector_tile.decode(x.response.content)
extent = content['occurrence']['extent'] # 512
print('extent:', extent)
data = content['occurrence']['features']
records = [
{
'x': item['geometry']['coordinates'][0],
'y': item['geometry']['coordinates'][1],
'total': item['properties']['total']
} for item in data
]
df = pd.DataFrame(records)
# conver x and y to mercator WGS84 Bounds: -180.0, -85.06, 180.0, 85.06
df['lon'] = (df['x'] - extent / 2) / (extent / 2) * 180
df['lat'] = (df['y'] - extent / 2) / (extent / 2) * 85.06
#df['lat'] = (df['y'] - extent / 2) / (extent / 2) * 90
# XY scatter plot
fig, ax = plt.subplots(figsize=(5, 5))
plt.scatter(df['x'], df['y'], c=df['total'], cmap=plt.cm.jet, s=0.1)
plt.xlim(0, extent)
plt.ylim(0, extent)
plt.title('xy coordinates')
plt.savefig('xy.png')
plt.show()
# Mercator scatter plot
fig, ax = plt.subplots(figsize=(5, 5))
plt.scatter(df['lon'], df['lat'], c=df['total'], cmap=plt.cm.jet, s=0.1)
plt.xlim(-180, 180)
plt.ylim(-90, 90)
plt.title('mercator coordinates')
plt.savefig('mercator.png')
plt.show()
fig, ax = plt.subplots(
figsize=(5, 5),
subplot_kw={'projection': ccrs.Orthographic(central_latitude=38, central_longitude=55)}
)
ax.add_feature(cfeature.LAND)
ax.add_feature(cfeature.OCEAN)
ax.scatter(
df['lon'], df['lat'],
c=df['total'], cmap=plt.cm.jet, s=1,
transform=ccrs.PlateCarree()
)
ax.set_global()
ax.spines['geo'].set_visible(False)
plt.title('ortho coordinates')
plt.savefig('ortho.png')
plt.show()
display(df)
Your data is provided in epsg 3857 not in epsg 4326!
So you have to convert the coordinates to x/y in epsg 3857 before re-projecting them to lon/lat in epsg 4326...
Changing the following lines will give you the proper coordinates:
import numpy as np
df['x_3857'] = (df['x'] - extent / 2) / (extent / 2) * np.pi * 6378137
df['y_3857'] = (df['y'] - extent / 2) / (extent / 2) * np.pi * 6378137
Here's a plot with eomaps to show that it worked:
from eomaps import Maps
m = Maps()
m.add_feature.preset("coastline", "ocean", "land")
m.set_data(df, x="x_3857", y="y_3857", parameter="total", crs=3857)
m.set_shape.rectangles()
m.plot_map(cmap="RdYlBu_r", set_extent=False)
m.add_colorbar(hist_kwargs=dict(log=True))