3D elevation on geographical map with python - 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,

Related

Plotting coordinate data on top of a map in Python matplotlib

So, what I am having trouble with is how I am supposed to plot the data I have on top of a global map. I have an array of data, and two arrays of coordinates in latitude and longitude, where each datapoint was taken, but I am not sure of how to plot it on top of a global map. Creating the map itself is not too difficult, I just use:
import matplotlib.pyplot as plt
from mpl_toolkits.basemap import Basemap
fig = plt.figure(figsize=(10, 8))
m = Basemap(projection='cyl', resolution='c',
llcrnrlat=-90, urcrnrlat=90,
llcrnrlon=-180, urcrnrlon=180, )
m.shadedrelief(scale=0.5)
m.drawcoastlines(color='black')
But the next step is where I am having problems. I have tried doing both a colormesh plot and scatter plot, but they haven't worked so far. How should I go about it so that the data is plotted in the correct coordinate locations for the global map?
Thanks a lot for any help!
Maybe a bit late, but I have this piece of code I used to plot multiple linear plot over a map in Basemap that worked for me.
map = Basemap(projection='cyl', resolution='c',
llcrnrlat=mins[1], urcrnrlat=maxs[1],
llcrnrlon=mins[0], urcrnrlon=50, )
plt.figure(figsize=(15, 15))
for i in range(1259):
filepath = filename[i]
data = pd.read_csv(filepath, index_col=0)
map.plot(data.x,data.y,'k-', alpha=0.1) ### Calling the plot in a loop!!
map.drawcoastlines(linewidth=1)
map.drawcountries(linewidth=0.5, linestyle='solid', color='k' )
plt.show()
The loop calls data from different folders, and I just use the map.plot command to plot. By doing it like that, you can plot all data in the same map.

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!

Basemap plotting a contour figure over coastlines

I am trying to superimpose a contour plot onto a basemap plot of coastlines. Right now it either plots both separately or just the basemap.
Xa = np.linspace(-93.6683,-93.2683,25)
Ya = np.linspace(29.005,29.405,25)
plt.figure()
m = Basemap(width=1200000,height=900000,projection='lcc',resolution='f',lat_1=29.205,lat_2=29.5,lat_0=29.205,lon_0=-93,4683)
m.drawcoastlines()
plt.contourf(Ya,Xa,Result.reshape(len(Xa),len(Ya)))
plt.colorbar()
plt.show()
The Result in the code are the concentrations that are plotted as a contour. I would add them, but there are 625 values for concentration from running my code.
Im wondering how I can write the plotting part of my code to be able to superimpose the two graphs. Thanks!
You would want to use basemap's contour function here. This has however some particularities.
It accepts only 2D arrays as input, meaning, you need to create a meshgrid of coordinates first.
and the input must be the mapped coordinates, x,y = m(X,Y)
Also pay attention to the dimensions. The first dimension of a numpy array is the y axis, the second dimension is the x axis.
from mpl_toolkits.basemap import Basemap
import matplotlib.pyplot as plt
import numpy as np
Xa = np.linspace(-93.6683,-93.2683,25)
Ya = np.linspace(29.005,29.405,25)
X,Y = np.meshgrid(Xa,Ya)
Result = np.random.rand(len(Ya)*len(Xa))
m = Basemap(width=1200000,height=900000,projection='lcc',resolution='c',
lat_1=29.205,lat_2=29.5,lat_0=29.205,lon_0=-93.4683)
m.drawcoastlines()
mx,my = m(X,Y)
m.contourf(mx,my,Result.reshape(len(Ya),len(Xa)))
plt.colorbar()
plt.show()

Plotting data set with extra points using matplotlib

I have searched stackoverflow to find an answer to my issue, but to no avail. I wish to plot an earthquake data set with a yellow star to represent the center of my study area. However, I can only plot the earthquake data and cannot plot the star. I have tried two solutions: just plotting both data sets such as what I have in my code, or using a subplot.
In other words, what is the equivalent of MatLab's hold on command in Python/matplotlib?
#!/usr/bin/env python
from mpl_toolkits.basemap import Basemap
import matplotlib.pyplot as plt
import numpy as np
# Read in latitudes and longitudes
eq_data = open('eq_data')
lats, lons = [], []
mag = []
for index, line in enumerate(eq_data.readlines()):
if index > 0:
lats.append(float(line.split(',')[0]))
lons.append(float(line.split(',')[1]))
mag.append(float(line.split(',')[2]))
#Build the basemap
antmap = Basemap(projection='spstere', boundinglat=10, lon_0=-60, resolution='f')
antmap.drawcoastlines(color='black', linewidth=0.15)
antmap.fillcontinents(color='0.95')
antmap.drawmapboundary(fill_color='aqua')
x,y = antmap(lons, lats)
x1,x2= (0,-90)
antmap.plot(x1,x2, 'r*', markersize=10)
antmap.plot(x,y,'ro', markersize=8)
plt.show()

Drawing a graph with NetworkX on a Basemap

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)

Categories

Resources