Long story short I can't get cartopy to install in my environment so I'm looking for alternative ways of doing things it might be used for.
I've recently been following this tutorial which uses cartopy to alter the path of shapely linestrings to take into account the curvature of the earth:
"Cartopy can be used to manipulate the way that lines are plotted. The transform=ccrs.Geodetic() method transforms the LineStrings to account for the earths curvature"
assuming I can just google the actual value that is the curvature of the earth, are there any ways I could manually manipulate the linestrings to achieve roughly the same effect?
It's probably feasible to implement the great circle algorithms yourself, but there are also other options. If you manage the install pyproj for example, you can use the example below, it samples a given amount of points between two locations on earth.
Note that although I still use Cartopy to show the coastlines (for reference), the actual plotting of the line (great circle) is fully independent of Cartopy and can be used with a plain Matplotlib axes as well.
And you can always read the coastlines or other borders/annotation etc for your map without Cartopy as well, as long as you pay attention to the projection. Matplotlib has all the primatives to do this (lines, PathCollections etc), it's just that Cartopy makes it a lot more convenient.
import matplotlib.pyplot as plt
import cartopy.crs as ccrs
from pyproj import Geod
# from
lat1 = 55.
lon1 = -65
# to
lat2 = 30
lon2 = 80
n_samples = 1000
g = Geod(ellps='WGS84')
coords = g.npts(
lon1, lat1, lon2, lat2, n_samples, initial_idx=0, terminus_idx=0,
)
lons, lats = zip(*coords)
fig, ax = plt.subplots(
figsize=(10,5), dpi=86, facecolor="w",
subplot_kw=dict(projection=ccrs.PlateCarree(), xlim=(-180,180), ylim=(-90, 90)),
)
ax.axis("off")
ax.coastlines(lw=.3)
ax.plot(lons, lats, "r-") # <- no cartopy, just x/y points!
The amount of points you sample should probably depend on the distance between both points, which can also be calculated using the g.inv(...) function. And the amount is a tradeoff between accuracy and performance. For most applications you probably want to keep it as low as possible, just above where you start to visibly see the effect.
Related
How can we increase the line width of plots produced by plot_implicit from Sympy? Neither I can find a parameter designated to this in the API, nor does Google give any expedient cues.
I've read that, e.g., plot_implicit(Eq(x1**2 + x2**2 - 1))._backend.ax references an Axis object of Matplotlib, so I tried changing the default line width of Matplotlib prior to invoking plot_implicit from Sympy, but it doesn't make any change:
from matplotlib import rcParams
rcParams['lines.linewidth'] = 10
In case that this isn't possible (which is hard to believe), what other options are there to produce implicit plots of functions in Python, such that the line width can be adapted?
From the plot_implicit documentation:
plot_implicit, by default, uses interval arithmetic to plot functions. If the expression cannot be plotted using interval arithmetic, it defaults to a generating a contour using a mesh grid of fixed number of points.
This means that the resulting plot is not a line plot, for which the line width can be set in any way.
The mesh gridding effect can also be observed when zooming into the plot:
I'm very new to focal mechanisms (beach balls) and would like to plot these on to a global map using Python.
I already have installed the Anaconda suite, and would like to know if I need to install any further modules to get this working (i.e. is the obspy module included in the Anaconda suite?
I won't be using any specific seismology files as input, but would simply like to create these beach balls by just manually inserting the corresponding parameters. So far I've come across two different examples of code:
from obspy.imaging.beachball import beachball
np1 = [150, 87, 1]
beachball(np1)
and...
from obspy.imaging.beachball import beachball
mt = [-2.39, 1.04, 1.35, 0.57, -2.94, -0.94]
beachball(mt)
It is not clear to me what each of these values refer to. I have an example of a site where I'd like to retrieve the relevant information from, then use these values as input for the beach ball:
http://earthquake.usgs.gov/earthquakes/eventpage/us20005ysu#moment-tensor
Thanks in advance.
Our Tutorial has examples of map plots including beachball patches:
http://docs.obspy.org/tutorial/code_snippets/basemap_plot_with_beachballs.html#basemap-plot-of-the-globe
Or check out our more up-to-date master branch Tutorial:
http://docs.obspy.org/master/tutorial/code_snippets/basemap_plot_with_beachballs.html
Pimping those examples you find there a bit you can arrive at this piece of code to plot moment tensors on a basemap (projection can be changed to your liking of course):
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.basemap import Basemap
from obspy import read_events
from obspy.imaging.beachball import beach
event = read_events(
'https://earthquake.usgs.gov/archive/product/moment-tensor/'
'us_20005ysu_mww/us/1470868224040/quakeml.xml', format='QUAKEML')[0]
origin = event.preferred_origin() or event.origins[0]
focmec = event.preferred_focal_mechanism() or event.focal_mechanisms[0]
tensor = focmec.moment_tensor.tensor
moment_list = [tensor.m_rr, tensor.m_tt, tensor.m_pp,
tensor.m_rt, tensor.m_rp, tensor.m_tp]
m = Basemap(projection='cyl', lon_0=origin.longitude, lat_0=origin.latitude,
resolution='c')
m.drawcoastlines()
m.fillcontinents()
m.drawparallels(np.arange(-90., 120., 30.))
m.drawmeridians(np.arange(0., 420., 60.))
m.drawmapboundary()
x, y = m(origin.longitude, origin.latitude)
ax = plt.gca()
b = beach(moment_list, xy=(x, y), width=20, linewidth=1, alpha=0.85)
b.set_zorder(10)
ax.add_collection(b)
plt.show()
You'll need the basemap package installed for this (conda install basemap).
Here, seismic source is described via moment tensor components
mt = [-2.39, 1.04, 1.35, 0.57, -2.94, -0.94]
Here, focal mechanism is described using nodal planes. There are two nodal planes - principal and auxiliary. In the example below, the principal one is given as input and auxiliary one is computed automatically. Orientation of the nodal plane is given with three angles [Strike, Dip, Rake]
np1 = [150, 87, 1]
According to opensha.org:
Strike:
Fault strike is the direction of a line created by the intersection of a fault plane and a horizontal surface, 0° to 360°, relative to North. Strike is always defined such that a fault dips to the right side of the trace when moving along the trace in the strike direction. The hanging-wall block of a fault is therefore always to the right, and the footwall block on the left. This is important because rake (which gives the slip direction) is defined as the movement of the hanging wall relative to the footwall block.
Dip:
Fault dip is the angle between the fault and a horizontal plane, 0° to 90°.
Rake:
Rake is the direction a hanging wall block moves during rupture, as measured on the plane of the fault. It is measured relative to fault strike, ±180°. For an observer standing on a fault and looking in the strike direction, a rake of 0° means the hanging wall, or the right side of a vertical fault, moved away from the observer in the strike direction (left lateral motion). A rake of ±180° means the hanging wall moved toward the observer (right lateral motion). For any rake>0, the hanging wall moved up, indicating thrust or reverse motion on the fault; for any rake<0° the hanging wall moved down, indicating normal motion on the fault.
I installed Obspy to Anaconda in this way
~/anaconda/bin/pip install obspy
After doing this the code you gave works fine:
from obspy.imaging.beachball import beachball
mt = [-2.39, 1.04, 1.35, 0.57, -2.94, -0.94]
beachball(mt)
Focal mechanism
Given land polygons as a Shapely MultiPolygon, I want to find the (Multi-)Polygon that represents the e.g. 12 nautical mile buffer around the coastlines.
Using the Shapely buffer method does not work since it uses euclidean calculations.
Can somebody tell me how to calculate geodesic buffers in python?
This is not a shapely problem, since shapely explicitly tells in its documentation that the library is for planar computation only. Nevertheless, in order to answer your question, you should specify the coordinate systems you are using for your multipolygons.
Assuming you are using WGS84 projection (lat,lon), this is a recipe I found in another SO question (fix-up-shapely-polygon-object-when-discontinuous-after-map-projection). You will need pyproj library.
import pyproj
from shapely.geometry import MultiPolygon, Polygon
from shapely.ops import transform as sh_transform
from functools import partial
wgs84_globe = pyproj.Proj(proj='latlong', ellps='WGS84')
def pol_buff_on_globe(pol, radius):
_lon, _lat = pol.centroid.coords[0]
aeqd = pyproj.Proj(proj='aeqd', ellps='WGS84', datum='WGS84',
lat_0=_lat, lon_0=_lon)
project_pol = sh_transform(partial(pyproj.transform, wgs84_globe, aeqd), pol)
return sh_transform( partial(pyproj.transform, aeqd, wgs84_globe),
project_pol.buffer(radius))
def multipol_buff_on_globe(multipol, radius):
return MultiPolygon([pol_buff_on_globe(g, radius) for g in multipol])
pol_buff_on_globe function does the following. First, build an azimuthal equidistant projection centered in the polygon centroid. Then, change the coordinate system of the polygon to that projection. After that, builds the buffer there, and then change the coordinate system of the buffered polygon to WGS84 coordinate system.
Some special care is needed:
You will need to find out how to translate the distance you want to the distance used in aeqd projection.
Be careful of not buffering including the poles (see the mentioned SO question).
The fact that we are using the centroid of the polygon to center the projection should guaranty the answer is good enough, but if you have specif precision requirements you should NOT USE this solution, or at least make a characterization of the error for the typical polygon you are using.
I'm brand new to Python, I just switched from Matlab. The distro is Anaconda 2.1.0 and I'm using the Spyder IDE that came with it.
I'm trying to make a scatter plot with equal ratios on the x and y axes, so that this code prints a square figure with the vertices of a regular hexagon plotted inside.
import numpy
import cmath
import matplotlib
coeff = [1,0,0,0,0,0,-1]
x = numpy.roots(coeff)
zeroplot = plot(real(x),imag(x), 'ro')
plt.gca(aspect='equal')
plt.show()
But plt.gca(aspect='equal') returns a blank figure with axes [0,1,0,1], and plt.show() returns nothing.
I think the main problem is that plt.gca(aspect='equal') doesn't just grab the current axis and set its aspect ratio. From the documentation, (help(plt.gca)) it appears to create a new axis if the current one doesn't have the correct aspect ratio, so the immediate fix for this should be to replace plt.gca(aspect='equal') with:
ax = plt.gca()
ax.set_aspect('equal')
I should also mention that I had a little bit of trouble getting your code running because you're using pylab to automatically load numpy and matplotlib functions: I had to change my version to:
import numpy
import cmath
from matplotlib import pyplot as plt
coeff = [1,0,0,0,0,0,-1]
x = numpy.roots(coeff)
zeroplot = plt.plot(numpy.real(x), numpy.imag(x), 'ro')
ax = plt.gca()
ax.set_aspect('equal')
plt.show()
People who are already comfortable with Python don't generally use Pylab, from my experience. In future you might find it hard to get help on things if people don't realise that you're using Pylab or aren't familiar with how it works. I'd recommend disabling it and trying to get used to accessing the functions you need through their respective modules (e.g. using numpy.real instead of just real)
I would like to add a scale bar (showing how big a micron is for example) to a mayavi plot I create with mlab.
For example, referencing this question: How to display a volume with non-cubic voxels correctly in mayavi
I can set the voxel size of a plot by using
from enthought.mayavi import mlab
import numpy as np
s=64
x,y,z = np.ogrid[0:s,0:s,0:s/2]
volume = np.sqrt((x-s/2)**2 + (y-s/2)**2 + (2*z-s/2)**2)
grid = mlab.pipeline.scalar_field(data)
grid.spacing = [1.0, 1.0, 2.0]
contours = mlab.pipeline.contour_surface(grid,
contours=[5,15,25], transparent=True)
mlab.show()
I would like an automated way of adding a some indicator of what the scale of the object I am showing is. Right now I am adding scale bars by hand with inkscape to exported images, but there has to be a better way.
A straightforward mayavi way would be most helpful, but if there is anything in vtk that would do it, I can always use mayavi's wrapper.
Something like text3d will let me add text, and then I suppose I could figure out how to draw a line as well and compute the correct scaling by hand, but I am hoping there is an easier way.
Try the following:
mlab.axes()
mlab.outline()
mlab.colorbar()
This reference: http://github.enthought.com/mayavi/mayavi/auto/mlab_reference.html would help as would the several examples.