Drawing a graph with NetworkX on a Basemap - python

I want to plot a graph on a map where the nodes would be defined by coordinates (lat, long) and have some value associated.
I have been able to plot points as a scatterplot on a basemap but can't seem to find how to plot a graph on the map.
Thanks.
EDIT: I have added code on how I plotted the points on a basemap. Most of it has been adapted from code in this article.
from mpl_toolkits.basemap import Basemap
from shapely.geometry import Point, MultiPoint
import pandas as pd
import matplotlib.pyplot as plt
m = Basemap(
projection='merc',
ellps = 'WGS84',
llcrnrlon=-130,
llcrnrlat=25,
urcrnrlon=-60,
urcrnrlat=50,
lat_ts=0,
resolution='i',
suppress_ticks=True)
# Create Point objects in map coordinates from dataframe lon
# and lat values
# I have a dataframe of coordinates
map_points = pd.Series(
[Point(m(mapped_x, mapped_y))
for mapped_x, mapped_y in zip(df['lon'],
df['lat'])])
amre_points = MultiPoint(list(map_points.values))
plt.clf()
fig = plt.figure()
ax = fig.add_subplot(111, axisbg='w', frame_on=False)
fig.set_size_inches(18.5, 10.5)
# Create a scatterplot on the map
dev = m.scatter(
[geom.x for geom in map_points],
[geom.y for geom in map_points],
20, marker='o', lw=.25,
facecolor='#33ccff', edgecolor='w',
alpha=0.9,antialiased=True,
zorder=3)
m.fillcontinents(color='#555555')
I get this image:

Here is one way to do it:
import networkx as nx
import matplotlib.pyplot as plt
from mpl_toolkits.basemap import Basemap as Basemap
m = Basemap(
projection='merc',
llcrnrlon=-130,
llcrnrlat=25,
urcrnrlon=-60,
urcrnrlat=50,
lat_ts=0,
resolution='i',
suppress_ticks=True)
# position in decimal lat/lon
lats=[37.96,42.82]
lons=[-121.29,-73.95]
# convert lat and lon to map projection
mx,my=m(lons,lats)
# The NetworkX part
# put map projection coordinates in pos dictionary
G=nx.Graph()
G.add_edge('a','b')
pos={}
pos['a']=(mx[0],my[0])
pos['b']=(mx[1],my[1])
# draw
nx.draw_networkx(G,pos,node_size=200,node_color='blue')
# Now draw the map
m.drawcountries()
m.drawstates()
m.bluemarble()
plt.title('How to get from point a to point b')
plt.show()

As of today there is a nice alternative to basemap. Mplleaflet is a library inspired by mpld3. It plots faster than basemap, is more easy to use and allows to visualizing geographic data on beautiful interactive openstreetmap. The input can be longitude and latitude the library automatically projects the data properly.
Input dictionary pos, where the node (country) is the key and long lat are saved as value.
pos = {u'Afghanistan': [66.00473365578554, 33.83523072784668],
u'Aland': [19.944009818523348, 60.23133494165451],
u'Albania': [20.04983396108883, 41.14244989474517],
u'Algeria': [2.617323009197829, 28.158938494487625],
.....
Plotting is as easy as:
import mplleaflet
fig, ax = plt.subplots()
nx.draw_networkx_nodes(GG,pos=pos,node_size=10,node_color='red',edge_color='k',alpha=.5, with_labels=True)
nx.draw_networkx_edges(GG,pos=pos,edge_color='gray', alpha=.1)
nx.draw_networkx_labels(GG,pos, label_pos =10.3)
mplleaflet.display(fig=ax.figure)

Related

3D elevation on geographical map with python

I am trying to display elevation/topography in 3D on a geographical map
I am currently displaying elevation with a colormap using the scatter function of matplolib over a geographical map created with the basemap package. I would like to visualize it in 3D with a shady effect or something similar.
Bellow is a simple example using data created randomly. The only constrain is to keep the 'ortho' look shown bellow. Any python package could be used.
Input data could either be a 1D arrays or 2D arrays.
import matplotlib.pyplot as plt
from mpl_toolkits.basemap import Basemap
import numpy as np
size = 1000
# data to plot
data = np.arange(size)*0.5/size
# coordinates
lat = np.random.uniform(low=65, high=90, size=(size,))
lon = np.random.uniform(low=-180, high=180, size=(size,))
f1, ax = plt.subplots(1, 1,figsize=(9,8))
m = Basemap(projection='ortho',lat_0=70,lon_0=0,resolution='l',ax=ax)
m.drawcoastlines(linewidth=0.25, zorder=0)
m.drawparallels(np.arange(90,-90,-5), labels=[1,1,1,1],linewidth = 0.25, zorder=1)
m.drawmeridians(np.arange(-180.,180.,30.),labels=[1,1,1,1],latmax=85, linewidth = 0.25, zorder=1)
m.fillcontinents(color='dimgray',lake_color='grey', zorder=1)
x,y = m(lon,lat)
cmap='viridis'
m.scatter(x,y,c=data,s=10,cmap=cmap,vmin=0,vmax=0.5,zorder=3,alpha=1)
plt.show()
Thanks a lot,

Plotting geographic data in 3d with matplotlib

I'm trying to plot data that contains lat, lon, and altitude as a 3d scatter plot in mpl. What I've found for documentation so far is either how to plot 2d geographic data using Basemap, OR how to plot 3d data using Axes3D, but not both. The specific coding issue I'm running into is how to set my lat/lon data to be interpreted as geographic lat and lon, but to keep my alt data as... well, altitude. I know Basemap contains the latlon setting:
"If latlon keyword is set to True, x,y are intrepreted as longitude
and latitude in degrees. Data and longitudes are automatically shifted
to match map projection region for cylindrical and pseudocylindrical
projections, and x,y are transformed to map projection coordinates."
However if I'm plotting in 3d, Axes3D doesn't support the latlon argument. The reason having geographic coordinates is so important is that I'm plotting the data over a basemap for visual reference.
My code:
import os
os.environ['PROJ_LIB'] = r'E:\Programs\Anaconda3\pkgs\proj4-5.2.0-ha925a31_1\Library\share'
import matplotlib as mpl
import matplotlib.pyplot as plt
import numpy as np
from mpl_toolkits.basemap import Basemap
from mpl_toolkits.mplot3d import Axes3D
fig = plt.figure()
ax = fig.gca(projection='3d')
# Define lower left, uperright lontitude and lattitude respectively
extent = [-180, 180, -90, 90]
# Create a basemap instance that draws the Earth layer
#bm = Basemap(llcrnrlon=extent[0], llcrnrlat=extent[2],
# urcrnrlon=extent[1], urcrnrlat=extent[3],
# projection='cyl', resolution='l', fix_aspect=False, ax=ax)
bm = Basemap(llcrnrlon=-73,llcrnrlat=41,urcrnrlon=-69.5,urcrnrlat=43.5,projection='lcc', resolution='i', lat_0=42, lon_0=-71, ax=ax, fix_aspect=True)
# Add Basemap to the figure
ax.add_collection3d(bm.drawcoastlines(linewidth=0.35))
ax.add_collection3d(bm.drawstates(linewidth=0.25))
#ax.add_collection3d(bm.drawcounties(linewidth=0.15))
#ax.set_axis_off()
ax.view_init(azim=230, elev=50)
ax.set_xlabel('Longitude (°E)', labelpad=20)
ax.set_ylabel('Latitude (°N)', labelpad=20)
ax.set_zlabel('Altitude (ft)', labelpad=20)
# Add meridian and parallel gridlines
#lon_step = 5
#lat_step = 5
#meridians = np.arange(extent[0], extent[1] + lon_step, lon_step)
#parallels = np.arange(extent[2], extent[3] + lat_step, lat_step)
#ax.set_yticks(parallels)
#ax.set_yticklabels(parallels)
#ax.set_xticks(meridians)
#ax.set_xticklabels(meridians)
ax.set_zlim(0., 50000.)
#ax.set_xlim(-69., -73.)
#ax.set_ylim(40.,44.)
# empty array for place holder
lons = np.array([]) # longtitude
lats = np.array([]) # latitude
alt = np.array([]) # altitude
# Make sure your working directory is the directory contains this script and the data file.
#directory = os.fsencode('.')
# Import data to illustrate
lons, lats, alt = np.loadtxt('adsb-csv-2019-07-07_xzyonly_small.csv', delimiter=',', unpack=True, skiprows=1)
#alons, alats = map(lons, lats, latlon=True)
# scatter map based on lons, lats, alts
p = ax.scatter(lons, lats, alt, c=alt, cmap='jet')
# Add a colorbar to reference the intensity
#fig.colorbar(p, label='Aircraft Altitude')
plt.show()
This was adapted from code written by Phúc Lê.
Any help would be much appreciated!

How to make spatial plot of irregular geographical data

I have lat=[13.7,21,23.7,10.6,34.5,20.7,33.1,15.5]
lon=[65.7,87.5,69.8,98.3,67,79.8,88.8,77.9] and
val=[234,310,287,279,298,280,279,321]
How can I make a spatial plot these data over map ? My code look like
lat=[13.7,21,23.7,10.6,34.5,20.7,33.1,15.5]
lon=[65.7,87.5,69.8,98.3,67,79.8,88.8,77.9]
val=[234,310,287,279,298,280,279,321]
lon, lat = np.meshgrid(lon, lat)
m = Basemap(projection='merc', resolution=None,
llcrnrlat=0, urcrnrlat=40,
llcrnrlon=60, urcrnrlon=100, )
m.contourf(lon,lat,val)
To be able to use contourf, you need gridded data (i.e. if you have an 8x8 lon-lat grid, you need 64 z values). As you have only (lon,lat,z) triplets, it is better to use a tricontourf plot. However, Basemap does not have that function, but has an additional tri keyword for the contourf function:
import numpy as np
from matplotlib import pyplot as plt
from mpl_toolkits import basemap
lat=np.array([13.7,21,23.7,10.6,34.5,20.7,33.1,15.5])
lon=np.array([65.7,87.5,69.8,98.3,67,79.8,88.8,77.9])
val=np.array([234,310,287,279,298,280,279,321])
#lon, lat = np.meshgrid(lon, lat) <-- do not use this
m = basemap.Basemap(projection='merc', resolution=None,
llcrnrlat=0, urcrnrlat=40,
llcrnrlon=60, urcrnrlon=100, )
##need to convert coordinates
x,y = m(lon,lat)
##add the `tri=True` kwarg
m.contourf(x,y,val, tri=True)
plt.show()

Basemap great circle longitudinal wrapping and missing data issue

I am attempting to plot multiple great circles using a for loop in conjunction with a set of lat/lon points. I am using the animation function with matplotlib to make the plots update when the data source is updated. This is all working well.
I noticed that plotting greatcircles where the shortest distance is wrapping the image, the plot will use that and appear on the other side of the map. Is there an argument that prevents this?
Also, depending on where the plot is I notice the "middle" of the plot arc is missing. What could be causing this? Map and code below:
The CSV uses the following points:(Moscow and Tokyo)
sourcelon sourcelat destlon destlat
55.44 37.51 -80.84 35.22
139 35.6 -80.84 35.22
Minimal code:
from mpl_toolkits.basemap import Basemap
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
import matplotlib.animation
# setup mercator map projection.
fig = plt.figure(figsize=(27, 20))
m = Basemap(projection='mill', lon_0=0)
m.drawcoastlines(color='r', linewidth=1.0)
def animate(i):
df = pd.read_csv('c:/python/scripts/test2.csv', sep='\s*,\s*',header=0, encoding='ascii', engine='python'); df
for x,y,z,w in zip(df['sourcelon'], df['sourcelat'], df['destlon'], df['destlat']):
line, = m.drawgreatcircle(x,y,z,w,color='r')
ani = matplotlib.animation.FuncAnimation(fig, animate, interval=1000)
plt.tight_layout()
plt.show()
As wikipedia tells us
The great-circle distance or orthodromic distance is the shortest distance between two points on the surface of a sphere, measured along the surface of the sphere.
So the path shown goes the shortest distance, which might wrap from one side of the image to the other.
The missing points in the line are a bit of a mystery, but it might be that there is some problem with the projection in use. Using a different projection, this works fine, e.g. projection='robin':
from mpl_toolkits.basemap import Basemap
import matplotlib.pyplot as plt
fig = plt.figure(figsize=(8,6))
m = Basemap(projection='robin',lon_0=0,resolution='c')
m.drawcoastlines(color='grey', linewidth=1.0)
a = [[55.44, 37.51, -80.84, 35.22],[139, 35.6, -80.84, 35.22]]
x,y,z,w = a[0]
line, = m.drawgreatcircle(x,y,z,w,color='r')
plt.show()
The problem can be circumvented if the distance between points in enlarged, e.g.
line, = m.drawgreatcircle(x,y,z,w,del_s=1000,color='r')
would give
Another workaround would be to get the data from the line and plot it manually,
from mpl_toolkits.basemap import Basemap
import matplotlib.pyplot as plt
fig = plt.figure(figsize=(8,6))
m = Basemap(projection='mill', lon_0=0)
m.drawcoastlines(color='grey', linewidth=1.0)
a = [[55.44, 37.51, -80.84, 35.22],[139, 35.6, -80.84, 35.22]]
x,y,z,w = a[0]
line, = m.drawgreatcircle(x,y,z,w,color='r')
line.remove()
mx,my = line.get_data()
m.plot(mx,my, color="limegreen")
plt.show()

Mapping with Shapely Polygons

I am at a loss to get the following code to work. For whatever reason, the GeoPandas *.plot() doesn't work, but I want to use both Pandas and GeoPandas for some simple plots.
I have been trying to take the Shapely objects from GeoPandas and plot them on a Basemap. The problem is that the polygons won't plot. I iterate through them from the GeoPandas.geometry, add them to the axes collection, and then use plot() - to no avail. Basemap seems to work fine, the code doesn't give any errors, but the polygons - counties - don't appear...
Thank you for the help!
import geopandas as gpd
from descartes import PolygonPatch
import matplotlib as mpl
import mpl_toolkits.basemap as base
import matplotlib.pyplot as plt
counties_file = r'C:\Users\...\UScounties\UScounties.shp'
counties = gpd.read_file(counties_file)
#new plot
fig = plt.figure(figsize=(5,5),dpi=300)
#ax = fig.add_subplot(111)
ax = ax = plt.gca()
minx, miny, maxx, maxy = counties.total_bounds
#map
m = base.Basemap(llcrnrlon=minx, llcrnrlat=miny,
urcrnrlon=maxx, urcrnrlat=maxy,
resolution='h', area_thresh=100000,
projection='merc')
patches = []
#add polygons
for poly in counties.geometry:
#deal with single polygons and multipolygons
if poly.geom_type == 'Polygon':
p = PolygonPatch(poly, facecolor='blue', alpha=1)
#plt.gca().add_patch(p)
#ax.add_patch(p)
patches.append(p)
elif poly.geom_type == 'MultiPolygon':
for single in poly:
q = PolygonPatch(single,facecolor='red', alpha=1)
#ax.add_patch(p)
patches.append(q)
m.drawcoastlines(linewidth=.1)
m.fillcontinents()
m.drawcountries(linewidth=.25,linestyle='solid')
m.drawstates(linewidth=.25,linestyle='dotted')
m.drawmapboundary(fill_color='white')
ax.add_collection(mpl.collections.PatchCollection(patches, match_original=True))
ax.plot()
plt.show()
Check if your shapefile is in the right projection system. Basemap is currently set to Mercator Projection. After that it worked for me.

Categories

Resources