Migrating from basemap to cartopy - coordinates offset? - matplotlib-basemap

I was happy with basemap showing some points of interest I have visited, but since basemap is dead, I am moving over to cartopy.
So far so good, I have transferred all the map features (country borders, etc.) without major problems, but I have noticed my points of interest are shifted a bit to North-East in cartopy.
Minimal example from the old basemap (showing two points of interest on the Germany-Czech border):
from mpl_toolkits.basemap import Basemap
import matplotlib.pyplot as plt
map = Basemap(projection='merc', epsg='3395',
llcrnrlon = 11.5, urcrnrlon = 12.5,
llcrnrlat = 50, urcrnrlat = 50.7,
resolution = 'f')
map.fillcontinents(color='Bisque',lake_color='LightSkyBlue')
map.drawcountries(linewidth=0.2)
lats = [50.3182289, 50.2523744]
lons = [12.1010486, 12.0906336]
x, y = map(lons, lats)
map.plot(x, y, marker = '.', markersize=1, mfc = 'red', mew = 1, mec = 'red', ls = 'None')
plt.savefig('example-basemap.png', dpi=200, bbox_inches='tight', pad_inches=0)
And the same example from cartopy - both points are slightly shifted to North-East as can be verified also e.g. via google maps:
import cartopy.crs as ccrs
import cartopy.feature as cfeature
import matplotlib.pyplot as plt
ax = plt.axes(projection=ccrs.Mercator())
ax.set_extent([11.5, 12.5, 50, 50.7])
ax.add_feature(cfeature.LAND.with_scale('10m'), color='Bisque')
ax.add_feature(cfeature.LAKES.with_scale('10m'), alpha=0.5)
ax.add_feature(cfeature.BORDERS.with_scale('10m'), linewidth=0.2)
lats = [50.3182289, 50.2523744]
lons = [12.1010486, 12.0906336]
ax.plot(lons, lats, transform=ccrs.Geodetic(),
marker = '.', markersize=1, mfc = 'red', mew = 1, mec = 'red', ls = 'None')
plt.savefig('cartopy.png', dpi=200, bbox_inches='tight', pad_inches=0)
Any idea how to get cartopy plot the points on expected coordinates? Tried cartopy 0.18 and 0.20 with the same result.

Related

GeoTIFF raster mirrored on Python basemap

I am trying to plot a .tif raster on my map with basemap. With QGIS I see the raster layer as it is supposed to be:
QGIS image
However, when then plotting with python basemap the colours are off, and the projection is somehow both rotated 180 degrees and mirrored, with a random blue line projected on the left side of the chart:
Some information about the .tif file (acquired with the rasterio and earthpy packages):
<osgeo.gdal.Dataset; proxy of <Swig Object of type 'GDALDatasetShadow *' at 0x7fcf9bdbef90> >
{'driver': 'GTiff', 'dtype': 'float32', 'nodata': -3.4028234663852886e+38, 'width': 2760, 'height': 1350, 'count': 1, 'crs': CRS.from_epsg(4326), 'transform': Affine(0.01, 0.0, 3.7,
0.0, -0.01, 71.2)}
EPSG:4326
BoundingBox(left=3.7, bottom=57.7, right=31.3, top=71.2)
+proj=longlat +datum=WGS84 +no_defs
The original raster data was downloaded here (forest restoration potential) and cropped to the right latitude and longitude with QGIS.
What am I doing wrong?
import gdal
from numpy import linspace
from numpy import meshgrid
import matplotlib.pyplot as plt
from mpl_toolkits.basemap import Basemap
lowlong = 3.7 #lower left corner of longitude
lowlat = 57.7 #lower left corner of latitude
upplong = 31.3 #upper right corner of longitude
upplat = 71.2 #upper right corner of latitude
pathToRaster = r'~/Data/Shapefile/NorwayPotential.tif'
raster = gdal.Open(pathToRaster,1)
print(raster)
geo = raster.GetGeoTransform()
geo = raster.ReadAsArray()
mp = Basemap(projection='merc',
llcrnrlon=lowlong,
llcrnrlat=lowlat,
urcrnrlon=upplong,
urcrnrlat=upplat,
resolution='i')
mp.drawcoastlines()
mp.drawcountries()
x = linspace(0,mp.urcrnrx,geo.shape[1])
y = linspace(0,mp.urcrnry,geo.shape[0])
xx,yy = meshgrid(x,y)
mp.pcolormesh(xx,yy,geo)
plt.show()
After the line of code:
geo = raster.ReadAsArray()
you can flip the data array by
geo = geo[::-1,:]
and should get the correct result.

fill oceans for basemap in 3D

I would like to fill oceans for my basemap in 3D but
ax.add_collection3d(m.drawmapboundary(fill_color='aqua'))
doesn't seem to work because the basemap drawmapboundary method doesn’t return an object supported by add_collection3d but a matplotlib.collections.PatchCollection object. Is there any workaround similar to the one done for land polygons here? Thank you!
Drawing a rectangle (polygon) below the map is one solution. Here is the working code that you may try.
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from mpl_toolkits.basemap import Basemap
from matplotlib.collections import PolyCollection
map = Basemap()
fig = plt.figure()
ax = Axes3D(fig)
ax.azim = 270
ax.elev = 50
ax.dist = 8
ax.add_collection3d(map.drawcoastlines(linewidth=0.20))
ax.add_collection3d(map.drawcountries(linewidth=0.15))
polys = []
for polygon in map.landpolygons:
polys.append(polygon.get_coords())
# This fills polygons with colors
lc = PolyCollection(polys, edgecolor='black', linewidth=0.3, \
facecolor='#BBAAAA', alpha=1.0, closed=False)
lcs = ax.add_collection3d(lc, zs=0) # set zero zs
# Create underlying blue color rectangle
# It's `zs` value is -0.003, so it is plotted below land polygons
bpgon = np.array([[-180., -90],
[-180, 90],
[180, 90],
[180, -90]])
polys2 = []
polys2.append(bpgon)
lc2 = PolyCollection(polys2, edgecolor='none', linewidth=0.1, \
facecolor='#445599', alpha=1.0, closed=False)
lcs2 = ax.add_collection3d(lc2, zs=-0.003) # set negative zs value
plt.show()
The resulting plot:

Basemap plus 3d graph

Hello Stackoverflow forks,
I'm a enthusiastic python learner.
I have studied python to visualiza my personal project about population density.
I have gone through tutorials about matplotlib and basemap in python.
I came across with the idea about
mapping my 3dimensional graph on top of the basemap which allows me to use geographycal coordinate information.
Can anyone let me know how I could use basemap as a base plane for the 3dimensional graph?
Please let me know which tutorial or references I could go with for developing this.
Best,
Thank you always Stackoverflow forks.
The basemap documentation has a small section on 3D plotting. Here's a simple script to get you started:
import matplotlib.pyplot as plt
from mpl_toolkits.basemap import Basemap
plt.close('all')
fig = plt.figure()
ax = fig.gca(projection='3d')
extent = [-127, -65, 25, 51]
# make the map and axis.
m = Basemap(llcrnrlon=extent[0], llcrnrlat=extent[2],
urcrnrlon=extent[1], urcrnrlat=extent[3],
projection='cyl', resolution='l', fix_aspect=False, ax=ax)
ax.add_collection3d(m.drawcoastlines(linewidth=0.25))
ax.add_collection3d(m.drawcountries(linewidth=0.25))
ax.add_collection3d(m.drawstates(linewidth=0.25))
ax.view_init(azim = 230, elev = 15)
ax.set_xlabel(u'Longitude (°E)', labelpad=10)
ax.set_ylabel(u'Latitude (°N)', labelpad=10)
ax.set_zlabel(u'Altitude (ft)', labelpad=20)
# values to plot - change as needed. Plots 2 dots, one at elevation 0 and another 100.
# also draws a line between the two.
x, y = m(-85.4808, 32.6099)
ax.plot3D([x, x], [y, y], [0, 100], color = 'green', lw = 0.5)
ax.scatter3D(x, y, 100, s = 5, c = 'k', zorder = 4)
ax.scatter3D(x, y, 0, s = 2, c = 'k', zorder = 4)
ax.set_zlim(0., 400.)
plt.show()

how can i calculate distances with pyephem?

hi i need a little help if any of you know how to calculate the distance of a coordinates and a satellite projection, i mean, when i predict the path of the satellite i need to know what is the distance between the future path and the coordinates that i put. and with that make a message alert notifiyng me when that satellite will be close to the coordinates.
this is the code that i am using any of you could help me that would be great.
from mpl_toolkits.basemap import Basemap
from geopy.distance import great_circle
from matplotlib import colors
from pyorbital import tlefile
import matplotlib.pyplot as plt
import numpy as np
import math
import ephem
from datetime import datetime
tlefile.TLE_URLS = ( 'http://celestrak.com/NORAD/elements/resource.txt',)
sat_tle = tlefile.read('NUSAT 1 (FRESCO)')
sat = ephem.readtle("NUSAT 1 (FRESCO)", sat_tle.line1, sat_tle.line2)
obs = ephem.Observer()
# location for tge coordinates
print("Latitud ")
sat_lat = input()
print("Longitud suggested point")
sat_lon = input()
obs.lat = str(sat_lat)
obs.long = str(sat_lon)
# programar proyeccion del mapa
map = Basemap(projection='ortho', lat_0=sat_lat, lon_0=sat_lon, resolution='l')
# draw coastlines, country boundaries, fill continents.
map.drawcoastlines(linewidth=0.25)
map.drawcountries(linewidth=0.25)
map.fillcontinents(color='coral',lake_color='aqua')
# draw the edge of the map projection region (the projection limb)
map.drawmapboundary(fill_color='aqua')
# grid in latitud and longitud every 30 sec.
map.drawmeridians(np.arange(0,360,30))
map.drawparallels(np.arange(-90,90,30))
# plot
passes = 4
for p in range(passes):
coords = []
dists = []
tr, azr, tt, altt, ts, azs = obs.next_pass(sat)
print """Date/Time (UTC) Alt/Azim Lat/Long Elev"""
print """====================================================="""
while tr < ts:
obs.date = tr
sat.compute(obs)
print "%s | %4.1f %5.1f | %4.1f %+6.1f | %5.1f" % \
(tr, math.degrees(sat.alt), math.degrees(sat.az), math.degrees(sat.sublat), math.degrees(sat.sublong), sat.elevation/1000.)
sat_lat = math.degrees(sat.sublat)
sat_lon = math.degrees(sat.sublong)
dist = great_circle((sat_lat, sat_lon), (sat_lat, sat_lon)).miles
coords.append([sat_lon, sat_lat])
dists.append(dist)
tr = ephem.Date(tr + 30.0 * ephem.second)
md = min(dists)
imd = 1 - (float(md) / 1400)
hue = float(240) / float(360)
clr = colors.hsv_to_rgb([hue, imd, 1])
map.drawgreatcircle(coords[0][0], coords[0][1], coords[-1][0], coords[-1][1], linewidth=2, color=clr)
obs.date = tr + ephem.minute
# map with UTC
date = datetime.utcnow()
cs=map.nightshade(date)
plt.title('next '+ str(passes)+ ' passes of the satellite')
plt.show()
You might want to look at http://rhodesmill.org/pyephem/quick.html#other-functions where it describes the function ephem.separation(). You are allowed to call it with two longitude, latitude coordinate pairs, and it will tell you how far apart they are:
ephem.separation((lon1, lat1), (lon2, lat2))
So if you pass the satellites's longitude and latitude as one of the coordinate pairs, and the longitude and latitude of the position you are interested in as the other, then you can watch for when the separation grows very small.

Wrong annotation text location on basemap

I try to annotate sampling position name on each scatter sample location based on mercator projection. I used sampling position as annotation location with map projection coordinate. But placed annotation location is wrong position on map.
Ex. When I annotate some position in pacific ocean, annotate text placed on Japan inland.
My map is below.
Here is my code.
from mpl_toolkits.basemap import Basemap
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1.inset_locator import inset_axes
from matplotlib.patches import Polygon
# create new figure, axes instances.
fig=plt.figure()
ax=fig.add_axes([0.1,0.1,0.8,0.8])
# setup mercator map projection.
m = Basemap(llcrnrlon=120.,llcrnrlat=10.,urcrnrlon=150.,urcrnrlat=44.,\
rsphere=(6378137.00,6356752.3142),\
resolution='l',projection='merc',\
lat_0=20.,lon_0=135.,lat_ts=25)
# nylat, nylon are lat/lon of Guam
nylat = 14; nylon = 140
# lonlat, lonlon are lat/lon of Busan.
lonlat = 35; lonlon = 128
# Draw Background Map
m.bluemarble()
m.drawgreatcircle(nylon,nylat,lonlon,lonlat,linewidth=2,color='b')
m.drawcoastlines()
m.fillcontinents()
# draw parallels
m.drawparallels(np.arange(10,50,10),labels=[1,1,0,1])
# draw meridians
m.drawmeridians(np.arange(120,155,10),labels=[1,1,0,1])
#m.drawmapscale(129, 15, 131, 16, 500, barstyle='fancy') drawing map scale
m.ax = ax
# Draw your dataset
lons = [130, 131, 132, 133]
lats = [21, 22, 23, 24]
Annotes = ['Sample #1', '2', '3', '4']
x, y = m(lons, lats)
#m.scatter(x1,y1,s=sizes,c=cols,marker="o",cmap=cm.cool,alpha=0.7)
m.scatter(x, y, s= 20, marker='*',color='y',alpha=0.7)
plt.annotate(Annotes[0], xy=(1110450, 1147253),xycoords='data',
xytext=(1110450, 1147253),textcoords='data',
arrowprops=dict(arrowstyle="->")
)
ax.set_title('Cruise line from Guam to Busan')
plt.show()
Thanks in advance. Any comments welcome!