Drawing Worldmap Whose Center Is Japan With Geopandas - python-3.x

Before reading my question, my english skill is poor, so please send me feedback or advise in easy words. Thank you.
What I wand to do:
I want to draw an worldmap whose center is Japan with geopandas library on python 3.x.
My Environment:
Windows10 (64bit)
Python v3.9.4
geopandas v0.9.0
My Code:
import geopandas
world = geopandas.read_file(geopandas.datasets.get_path("naturalearth_lowres"))
world.boundary.plot(figsize=(15,8))
The Obtained Image
world image
Question:
The center of an obtained worldmap drawing is arouond Africa. I want to draw the image whose center is Japan. I read an official document: Mapping and Plotting Tools, but I can not find how to realize it. Please tell me advices !!

I found working with geopandas (+ pyproj as its dependency) to get the shifted map is too difficult. In my code below, geopandas is used to provide the geodataframe of the world to manipulate and plot. Cartopy is used to provide the geoaxis for proper geospatial referencing. And shapely is used to do all sorts of manipulation to transform geometries for plotting re-centered world plot to meet the requirements in the question.
import matplotlib.pyplot as plt
import cartopy.crs as ccrs
from shapely.geometry import LineString, MultiPolygon, Polygon
from shapely.ops import split
from shapely.affinity import translate
from cartopy.mpl.gridliner import LONGITUDE_FORMATTER, LATITUDE_FORMATTER
import geopandas
def shift_map(world_gdf, shift, ax):
# world_gdf: world geodataframe to shift
# shift: longitude shift
# ax: geoaxis to plot the map
shift -= 180
moved_map = []
splitted_map = []
border = LineString([(shift,90),(shift,-90)])
for row in world_gdf["geometry"]:
splitted_map.append(split(row, border))
for element in splitted_map:
items = list(element)
for item in items:
minx, miny, maxx, maxy = item.bounds
if minx >= shift:
moved_map.append(translate(item, xoff=-180-shift))
else:
moved_map.append(translate(item, xoff=180-shift))
gdf = geopandas.GeoDataFrame({"geometry": moved_map})
gdf.boundary.plot(ax=ax, linewidth=1, color='gray')
# can also use: gdf.plot() to plot the geometries as polygons
# define CRS's
crs0 = ccrs.PlateCarree(central_longitude=0) # standard CRS
lon_0 = 138 # Japan at center
# crsJapan = ccrs.PlateCarree(central_longitude=lon_0) # japan's centered; not in-use
# a special CRS for use with ax1.gridlines() to get correct longitude's labels plot
crsGridLines = ccrs.PlateCarree(central_longitude=-lon_0)
# create figure, axis
# use cartopy ccrs to get some niceties
fig, ax1 = plt.subplots(figsize=(8, 4.5), subplot_kw={"projection": crs0})
# load world geodataframe
world = geopandas.read_file(geopandas.datasets.get_path("naturalearth_lowres"))
# Plot the shifted map
shift_map(world, lon_0, ax1)
# Plot graticule/grid; only work with geoaxis
gl = ax1.gridlines(crs=crsGridLines, draw_labels=True, linewidth=1, color='gray', linestyle='--')
gl.xformatter = LONGITUDE_FORMATTER
gl.yformatter = LATITUDE_FORMATTER
gl.xlabel_style = {'size': 10, 'color': 'black'}
gl.ylabel_style = {'size': 10, 'color': 'black'}
plt.show()

Related

Python: how to create a smoothed version of a 2D binned "color map"?

I would like to create a version of this 2D binned "color map" with smoothed colors.
I am not even sure this would be the correct nomenclature for the plot, but, essentially, I want my figure to be color coded by the median values of a third variable for points that reside in each defined bin of my (X, Y) space.
Even though I am able to accomplish that to a certain degree (see example), I would like to find a way to create a version of the same plot with a smoothed color gradient. That would allow me to visualize the overall behavior of my distribution.
I tried ideas described here: Smoothing 2D map in python
and here: Python: binned_statistic_2d mean calculation ignoring NaNs in data
as well as links therein, but could not find a clear solution to the problem.
This is what I have so far:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import cm
from scipy.stats import binned_statistic_2d
import random
random.seed(999)
x = np.random.normal (0,10,5000)
y = np.random.normal (0,10,5000)
z = np.random.uniform(0,10,5000)
fig = plt.figure(figsize=(20, 20))
plt.rcParams.update({'font.size': 10})
ax = fig.add_subplot(3,3,1)
ax.set_axisbelow(True)
plt.grid(b=True, lw=0.5, zorder=-1)
x_bins = np.arange(-50., 50.5, 1.)
y_bins = np.arange(-50., 50.5, 1.)
cmap = plt.cm.get_cmap('jet_r',1000) #just a colormap
ret = binned_statistic_2d(x, y, z, statistic=np.median, bins=[x_bins, y_bins]) # Bin (X, Y) and create a map of the medians of "Colors"
plt.imshow(ret.statistic.T, origin='bottom', extent=(-50, 50, -50, 50), cmap=cmap)
plt.xlim(-40,40)
plt.ylim(-40,40)
plt.xlabel("X", fontsize=15)
plt.ylabel("Y", fontsize=15)
ax.set_yticks([-40,-30,-20,-10,0,10,20,30,40])
bounds = np.arange(2.0, 20.0, 1.0)
plt.colorbar(ticks=bounds, label="Color", fraction=0.046, pad=0.04)
# save plots
plt.savefig("Whatever_name.png", bbox_inches='tight')
Which produces the following image (from random data):
Therefore, the simple question would be: how to smooth these colors?
Thanks in advance!
PS: sorry for excessive coding, but I believe a clear visualization is crucial for this particular problem.
Thanks to everyone who viewed this issue and tried to help!
I ended up being able to solve my own problem. In the end, it was all about image smoothing with Gaussian Kernel.
This link: Gaussian filtering a image with Nan in Python gave me the insight for the solution.
I, basically, implemented the exactly same code, but, in the end, mapped the previously known NaN pixels from the original 2D array to the resulting smoothed version. Unlike the solution from the link, my version does NOT fill NaN pixels with some value derived from the pixels around. Or, it does, but then I erase those again.
Here is the final figure produced for the example I provided:
Final code, for reference, for those who might need in the future:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import cm
from scipy.stats import binned_statistic_2d
import scipy.stats as st
import scipy.ndimage
import scipy as sp
import random
random.seed(999)
x = np.random.normal (0,10,5000)
y = np.random.normal (0,10,5000)
z = np.random.uniform(0,10,5000)
fig = plt.figure(figsize=(20, 20))
plt.rcParams.update({'font.size': 10})
ax = fig.add_subplot(3,3,1)
ax.set_axisbelow(True)
plt.grid(b=True, lw=0.5, zorder=-1)
x_bins = np.arange(-50., 50.5, 1.)
y_bins = np.arange(-50., 50.5, 1.)
cmap = plt.cm.get_cmap('jet_r',1000) #just a colormap
ret = binned_statistic_2d(x, y, z, statistic=np.median, bins=[x_bins, y_bins]) # Bin (X, Y) and create a map of the medians of "Colors"
sigma=1 # standard deviation for Gaussian kernel
truncate=5.0 # truncate filter at this many sigmas
U = ret.statistic.T.copy()
V=U.copy()
V[np.isnan(U)]=0
VV=sp.ndimage.gaussian_filter(V,sigma=sigma)
W=0*U.copy()+1
W[np.isnan(U)]=0
WW=sp.ndimage.gaussian_filter(W,sigma=sigma)
np.seterr(divide='ignore', invalid='ignore')
Z=VV/WW
for i in range(len(Z)):
for j in range(len(Z[0])):
if np.isnan(U[i][j]):
Z[i][j] = np.nan
plt.imshow(Z, origin='bottom', extent=(-50, 50, -50, 50), cmap=cmap)
plt.xlim(-40,40)
plt.ylim(-40,40)
plt.xlabel("X", fontsize=15)
plt.ylabel("Y", fontsize=15)
ax.set_yticks([-40,-30,-20,-10,0,10,20,30,40])
bounds = np.arange(2.0, 20.0, 1.0)
plt.colorbar(ticks=bounds, label="Color", fraction=0.046, pad=0.04)
# save plots
plt.savefig("Whatever_name.png", bbox_inches='tight')

How to retrieve a gridline axis tick positions?

I am trying to retrieve the yaxis and xaxis tick positions from a cartopy geoaxes.
As far as I understand, a common matplotlib's Axes has the internal method: 'axes.get_xticks' and 'axes.get_yticks'.
Nevertheless, a cartopy's gridline from a geoaxes does not. How could I retrieve them?
Also, when I try to retrieve the ticks from a geoaxes using the common format (i.e.: "axes.get_yticks"), I end up with strange coordinates.
Here is an example.
import pandas as pd
pd.set_option('display.width', 50000)
pd.set_option('display.max_rows', 50000)
pd.set_option('display.max_columns', 5000)
import numpy as np
import matplotlib.pyplot as plt
import cartopy.crs as ccrs
import cartopy.feature as cfeature
from matplotlib.offsetbox import AnchoredText
def main(projection = ccrs.Mercator(), drawlicense=True):
fig = plt.figure(figsize=(9,7))
ax = plt.axes(projection=projection)
# Put a background image on for nice sea rendering.
ax.stock_img()
# Create a feature for States/Admin 1 regions at 1:50m from Natural Earth
states_provinces = cfeature.NaturalEarthFeature(
category='cultural',
name='admin_1_states_provinces_lines',
scale='50m',
facecolor='none')
SOURCE = 'Natural Earth'
LICENSE = 'public domain'
ax.add_feature(cfeature.LAND)
ax.add_feature(cfeature.COASTLINE)
ax.add_feature(states_provinces, edgecolor='gray')
# Add a text annotation for the license information to the
# the bottom right corner.
if drawlicense:
text = AnchoredText(r'$\mathcircled{{c}}$ {}; license: {}'
''.format(SOURCE, LICENSE),
loc='right',
bbox_transform=ax.transAxes,
bbox_to_anchor=(1.01, -0.02),
prop={'size': 8},
frameon=False)
ax.add_artist(text)
plt.show()
return ax
ax = main()
Gridliner = ax.gridlines(draw_labels=True)
In this above case, if I try to retrieve the yticks from the geoaxes "ax", I end up with an array of strange values as:
In: ax.get_yticks()
Out:
array([-20000000., -15000000., -10000000., -5000000., 0.,
5000000., 10000000., 15000000., 20000000.])
Notice that the values are not in degrees, though the figure and also the selected cartopy's projection states degree coordinates.
Therefore, what am I doing wrong? How can I get the respective degree coordinates of the map?
Sincerely,
The cartopy axes does not actually show normal matplotlib ticks. Instead you can use ax.gridlines to obtain a set of linecollections that show the grid. The returned cartopy.mpl.gridliner.Gridliner can be used to query the positions of the lines.
Note that projections are not necessarily separable in x and y, hence the gridlines could potentially be curves.
In the following we take the first points of those lines.
# create grid
gridliner = ax.gridlines(draw_labels=True)
# we need to draw the figure, such that the gridlines are populated
fig.canvas.draw()
ysegs = gridliner.yline_artists[0].get_segments()
yticks = [yseg[0,1] for yseg in ysegs]
xsegs = gridliner.xline_artists[0].get_segments()
xticks = [xseg[0,0] for xseg in xsegs]
print(xticks)
print(yticks)
This prints two lists with the first gridline point's coordinates:
[-180.0, -120.0, -60.0, 0.0, 60.0, 120.0]
[-80.0, -60.0, -40.0, -20.0, 0.0, 20.0, 40.0, 60.0, 80.0, 100.0]

3D Dicom Visualisation in Python

I am new to 3D image processing . I would like to know how to view the dicom series in python. I tried using matplotlib and VTK. In matplot I am not able to view the volume like I view in matlab using volViewer. Regarding VTK I am not able to import VTKRAyCASt for viewing 3D. The version I am using is 8.2.0.
I am doing the processing using scipy.ndimages
Kindly suggest me some resources to my volume dicom files
You can try ipyvolume https://github.com/maartenbreddels/ipyvolume for interactive plotting, I found it quite useful.
Also, you can plot them with matplotlib by using marching cubes to obtain the surface mesh but it is quite slow though:
from mpl_toolkits.mplot3d.art3d import Poly3DCollection
import numpy as np
from skimage import measure
def plot_3d(image, threshold=-300):
p = image.transpose(2,1,0)
verts, faces, normals, values = measure.marching_cubes_lewiner(p, threshold)
fig = plt.figure(figsize=(10, 10))
ax = fig.add_subplot(111, projection='3d')
mesh = Poly3DCollection(verts[faces], alpha=0.1)
face_color = [0.5, 0.5, 1]
mesh.set_facecolor(face_color)
ax.add_collection3d(mesh)
ax.set_xlim(0, p.shape[0])
ax.set_ylim(0, p.shape[1])
ax.set_zlim(0, p.shape[2])
plt.show()
The threshold of -300 HU is fine for visualizing chest CT scans but change it if you going to use MRI (check your intensity values distribution) or binary volumes (threshold =0).
There is some example of visualization:
With vtkplotter you should be able to do this easily:
from vtkplotter import *
volume = load(mydicomdir) #returns a vtkVolume object
show(volume, bg='white')
To install:
pip install vtkplotter

I want to generate a mesh from a point cloud in Python

I have a point cloud from different parts of the human body, like an eye and I want to do a mesh. I tried to use Mayavi and Delaunay but I don't get a good mesh. The points of the cloud are in total disorder.
I have my point cloud in .npz file
Using Mayavi
Then I want to save my model in an obj or stl file, but first I want to generate the mesh.
What do you recommend me to use, do I need a special library?
You can use pyvista to do the 3D interpolation. You need however to manually play with the alpha parameter that controls the distance under which two points are linked.
import numpy as np
import pyvista as pv
# points is a 3D numpy array (n_points, 3) coordinates of a sphere
cloud = pv.PolyData(points)
cloud.plot()
volume = cloud.delaunay_3d(alpha=2.)
shell = volume.extract_geometry()
shell.plot()
Data
Let's use the capitals of Europe. We read them in from Excel with Pandas:
import pandas as pd
dg0 = pd.read_excel('psc_StaedteEuropa_coord.xlsx') # ,header=None
dg0.head()
City Inhabit xK yK
0 Andorra 24574.0 42.506939 1.521247
1 Athen 664046.0 37.984149 23.727984
2 Belgrad 1373651.0 44.817813 20.456897
3 Berlin 3538652.0 52.517037 13.388860
4 Bern 122658.0 46.948271 7.451451
Grid by triangulation
We use Scipy for that. For a 3-dim example see HERE and HERE or here (CGAL has a Python wrapper)
import numpy as np
from scipy.spatial import Delaunay
yk, xk, city = np.array(dg0['xK']), np.array(dg0['yK']), np.array(dg0['City'])
X1 = np.vstack((xk,yk)).T
tri = Delaunay(X1)
Graphics
import cartopy.crs as ccrs
import matplotlib.pyplot as plt
#--- grafics -------
figX = 25; figY = 18
fig1 = plt.figure(figsize=(figX, figY), facecolor='white')
myProjection = ccrs.PlateCarree()
ax = plt.axes(projection=myProjection)
ax.stock_img()
ax.set_extent([-25, 40, 35, 65], crs=myProjection)
plt.triplot(X1[:,0], X1[:,1], tri.simplices.copy(), color='r', linestyle='-',lw=2)
plt.plot(X1[:,0], X1[:,1], 's', color='w')
plt.scatter(xk,yk,s=1000,c='w')
for i, txt in enumerate(city):
ax.annotate(txt, (X1[i,0], X1[i,1]), color='k', fontweight='bold')
plt.savefig('Europe_A.png')
plt.show()
If your points are "are in total disorder", and if you want to generate a mesh, then you need some interpolation from the cloud of points to the somehow structured grid points of the mesh..
In the 2-dimensional case matplotlib's triangulation can be a help:
matplotlib's triangulation 2dim.
In the 3-dimensional case there are 2 options. Depending on the data, you might want to interpolate them to a 3-dimensional surface. Then matplotlib's trisurf3d can be a help.
If you need a 3-dimensional volume grid then you have probably to look for a FEM (finite element) grid, e.g. FEnics
An example of interpolating a 3-dimensional field with scipy for contouring can be found here
Have you tried this example? https://docs.enthought.com/mayavi/mayavi/auto/example_surface_from_irregular_data.html
The relevant part is here
# Visualize the points
pts = mlab.points3d(x, y, z, z, scale_mode='none', scale_factor=0.2)
# Create and visualize the mesh
mesh = mlab.pipeline.delaunay2d(pts)
surf = mlab.pipeline.surface(mesh)

How To Change Colours In Plotly 3D Scatter

I'm learning how to plot 3D scatters on Plotly using their example with my own data. Sample here
I can get the scatter to plot (it looks so cool) but I can't get the different data series points to appear as separate colours.
import plotly #load plotly for plotting
import plotly.plotly as py
from plotly import tools
from plotly.graph_objs import * #all the types of plots that we will plot here
plotly.offline.init_notebook_mode() # run at the start of every ipython notebook
trace1 = Scatter3d(
x = res,
y = lc,
z = spent,
mode='markers',
marker=dict(
size=12,
color=["z","y","x"], # set color to an array/list of desired values
colorscale='Viridis', # choose a colorscale
opacity=0.8
)
)
data = [trace1]
layout = Layout(
margin=dict(
l=0,
r=0,
b=0,
t=0
)
)
fig = Figure(data=data, layout=layout)
plotly.offline.iplot(fig, filename='3d-scatter-colorscale')
I've tried using other examples for separate sources e.g. Cambridge Spark but I just don't have the experience to figure out how to make it work.
I'm sure it's something simple that I've missed but can't see it.
I should've had one of my series as the marker point. In this case I used spent instead of a list of x, y, z
import plotly #load plotly for plotting
import plotly.plotly as py
from plotly import tools
from plotly.graph_objs import * #all the types of plots that we will plot here
plotly.offline.init_notebook_mode() # run at the start of every ipython notebook
trace1 = Scatter3d(
x = res,
y = lc,
z = spent,
mode='markers',
marker=dict(
size=12,
color=spent, # set color to an array/list of desired values
colorscale='Viridis', # choose a colorscale
opacity=0.8
)
)
data = [trace1]
layout = Layout(
margin=dict(
l=0,
r=0,
b=0,
t=0
)
)
fig = Figure(data=data, layout=layout)
plotly.offline.iplot(fig, filename='3d-scatter-colorscale')

Resources