I am trying to do a basic plot of the world map using Python and the Matplotlib library. However, when I plot the polygons the plot shows many straight lines that do not seem to be part of the polygon. I am relatively new at working with shapefiles but the code I'm using has worked for a previous shapefile I used, so I'm confused and wondering what might be missing in the code.
The code I'm using is:
import numpy as np
import pandas as pd
import shapefile as shp
import matplotlib.pyplot as plt
import seaborn as sns
import os
sns.set(style='whitegrid', palette='ocean', color_codes=True)
sns.mpl.rc('figure', figsize=(10,6))
sf = shp.Reader(shp_path)
def plot_map(sf, x_lim = None, y_lim = None, figsize = (11,9)):
'''
Plot map with lim coordinates
'''
plt.figure(figsize = figsize)
id=0
for shape in sf.shapeRecords():
x = [i[0] for i in shape.shape.points[:]]
y = [i[1] for i in shape.shape.points[:]]
plt.plot(x, y, 'k')
if (x_lim == None) & (y_lim == None):
x0 = np.mean(x)
y0 = np.mean(y)
plt.text(x0, y0, id, fontsize=10)
id = id+1
if (x_lim != None) & (y_lim != None):
plt.xlim(x_lim)
plt.ylim(y_lim)
plot_map(sf)
plt.show()
The following link shows resulting graph (I'm not allowed to post pictures yet?):
Any help is appreciated, thank you all!
pls use 'k.', or use scatter instead of plot
import numpy as np
import pandas as pd
import shapefile as shp
import matplotlib.pyplot as plt
import seaborn as sns
import os
sns.set(style='whitegrid', palette='ocean', color_codes=True)
sns.mpl.rc('figure', figsize=(10,6))
sf = shp.Reader(shp_path)
def plot_map(sf, x_lim = None, y_lim = None, figsize = (11,9)):
'''
Plot map with lim coordinates
'''
plt.figure(figsize = figsize)
id=0
for shape in sf.shapeRecords():
x = [i[0] for i in shape.shape.points[:]]
y = [i[1] for i in shape.shape.points[:]]
## change here
plt.plot(x, y, 'k.')
if (x_lim == None) & (y_lim == None):
x0 = np.mean(x)
y0 = np.mean(y)
plt.text(x0, y0, id, fontsize=10)
id = id+1
if (x_lim != None) & (y_lim != None):
plt.xlim(x_lim)
plt.ylim(y_lim)
plot_map(sf)
plt.show()
Related
I am importing CSV data in the format x,y,z,p to plot a trisurface which has the scatter plots displayed on top.
The trisurface script works (ax.plot_trisurf), however, I would like to colour the scatter points (ax.scatter) according to either the 1 or -1 assigned in the fourth column of the CSV file.
enter image description here
The x,y,z data is complicated and can't be coloured, hence trying to assign it as simply as possible in the fourth column.
I have attached a basic image, essentially I just want to be able to have a selection of the red dots a different colour without affecting the trisurface they are on.
Any comments or suggestions are be very welcome!
My most recent error is:
ax.scatter(X, Y, np.log10(Z), c= (not p <= (0)({True: 'g', False: 'r'})), marker='o')
TypeError: 'int' object is not callable
enter code here
from typing import Any
import matplotlib
from mpl_toolkits.mplot3d import axes3d
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import sys
import csv
import bbox
import matplotlib.ticker as mticker
# Import CSV data
from numpy import ndarray
csvFileName = sys.argv[0]
csvData = []
with open('ParvaluesMESS.csv', 'r') as csvfile:
csvReader = csv.reader(csvfile, delimiter=',')
for csvRow in csvReader:
csvData.append(csvRow)
csvData = np.array(csvData)
csvData = csvData.astype(float)
X, Y, Z, p = csvData[:,0], csvData[:,1], csvData[:,2], csvData[:,3]
# Plot management: note Z is logged
# Change vmin and vmax values for colorbar range if needed
# Alpha value is transparency
# 111 means 1x1 grid, first subplot
fig = plt.figure(figsize=(5,5))
ax = fig.add_subplot(111, projection='3d')
cb = ax.plot_trisurf(X, Y, np.log10(Z), cmap='coolwarm', alpha=0.75)
#cb = ax.plot_trisurf(X, Y, np.log10(Z), cmap='coolwarm', alpha=0.75, vmin=0, vmax=1)
ax.scatter(X, Y, np.log10(Z), col==(p > 0({True: 'g', False: 'r'})), marker='o')
#ax.zaxis._set_scale('log')
def log_tick_formatter(val, pos=None):
"""Reformat log ticks for display"""
return f"$10^{{{int(val)}}}$"
# Set Z axis to log
ax.zaxis.set_major_formatter(mticker.FuncFormatter(log_tick_formatter))
# ax.zaxis.set_major_locator(mticker.MaxNLocator(integer=True))
def ticklabels(ticks):
ticks_labels = []
for i in ticks:
ticks_labels.append(f'2^{np.log2(i)}')
return ticks_labels
fig.colorbar(cb, shrink=0.5)
ax.set_title("First-year sea ice PAR")
ax.set_xlabel("Ice Thickness m")
ax.set_ylabel("Snow thickness m")
ax.set_zlabel("µmol $^{m-2}$ $^{s-1}$")
ax.view_init(azim=70, elev=30)
ax.set_xlim3d(20, 350)
image_format = 'png' # e.g .png, .svg, etc.
image_name = 'test.eps'
plt.show()
fig.savefig(image_name, format=image_format, dpi=1200)
it was resolved by rearranging into arrays:
from typing import Any
import matplotlib
from mpl_toolkits.mplot3d import axes3d
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import sys
import matplotlib.ticker as mticker
from numpy import ndarray
data=pd.read_csv('ParvaluesMESS.csv',header=None,sep=',',names=.
['Ice','Snow','umol','P'])
x=data[['Ice']].to_numpy()
y=data[['Snow']].to_numpy()
z=data[['umol']].to_numpy()
p=data[['P']].to_numpy()
x=(x.astype(float)).flatten()
y=(y.astype(float)).flatten()
z=(z.astype(float)).flatten()
p=(p.astype(float)).flatten()
fig = plt.figure(figsize=(5, 5))
ax = fig.add_subplot(111, projection='3d')
cb = ax.plot_trisurf(x, y, np.log10(z), cmap='coolwarm', alpha=0.75)
ax.scatter(x, y, np.log10(z),c=p,cmap='RdYlGn')
#your formats
def log_tick_formatter(val, pos=None):
"""Reformat log ticks for display"""
return f"$10^{{{int(val)}}}$"
# Set Z axis to log
ax.zaxis.set_major_formatter(mticker.FuncFormatter(log_tick_formatter))
# ax.zaxis.set_major_locator(mticker.MaxNLocator(integer=True))
def ticklabels(ticks):
ticks_labels = []
for i in ticks:
ticks_labels.append(f'2^{np.log2(i)}')
return ticks_labels
fig.colorbar(cb, shrink=0.5)
ax.set_title("First-year sea ice PAR")
ax.set_xlabel("Ice Thickness m")
ax.set_ylabel("Snow thickness m")
ax.set_zlabel("µmol $^{m-2}$ $^{s-1}$")
ax.view_init(azim=70, elev=30)
ax.set_xlim3d(20, 350)
image_name = 'BenImag'
image_format = 'png' # e.g .png, .svg, etc.
plt.show()
fig.savefig(image_name, format=image_format, dpi=1200)
I want to divide and color points,val_lab(611,3) by their labels,pred_LAB(611,)
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
fig = plt.figure()
ax = plt.axes(projection = '3d')
ax.set_xlabel('L')
ax.set_ylabel('A')
ax.set_zlabel('B')
for i in range(0, len(val_lab)):
ax.scatter3D(
val_lab[i,0],
val_lab[i,1],
val_lab[i,2],
s = 8,
marker='o',
c = pred_LAB
#cmap = 'rainbow'
)
#ax.legend(*points.legend_elements(), title = 'clusters')
plt.show()
The problem is it shows error,
c' argument has 611 elements, which is not acceptable for use with 'x'
with size 1, 'y' with size 1.
However, if the dataset only have ten points,it can show the figure correctly, I don't know how to solve this problem, besides, how to add legend of this figure?
In your solution you would want to replace c = pred_LAB with c = pred_LAB[i]. But you do not have to use a for loop to plot the data. You can just use the following:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
# generate random input data
val_lab = np.random.randint(0,10,(611,3))
pred_LAB = np.random.uniform(0,1, (611,))
# plot data
fig = plt.figure()
ax = plt.axes(projection = '3d')
ax.set_xlabel('L')
ax.set_ylabel('A')
ax.set_zlabel('B')
# create one 3D scatter plot - no for loop
ax.scatter3D(
val_lab[:,0],
val_lab[:,1],
val_lab[:,2],
s = 8,
marker='o',
c = pred_LAB,
cmap = 'rainbow',
label='my points'
)
# add legend
plt.legend()
plt.show()
I want to show the value of a 0 or 1 array on a plot with other timeseries.
How can I achieve something like the grey lines below - except mine will oscillate a lot more.
series.
For example, how to add osc here:
import numpy as np
import matplotlib.pyplot as plt
import datetime
import pandas as pd
n = 100
x = range(n)
y = np.random.rand(100)
osc = np.random.randint(2, size=n)
plt.plot(x,y)
plt.show(block=True)
Well, you can loop through the values and call axvspan(x0,x1,color=...,alpha=...);
import numpy as np
import matplotlib.pyplot as plt
n = 100
x = range(n)
y = np.random.randn(100).cumsum()
osc = np.random.randint(2, size=n)
plt.plot(x, y, color='crimson')
for x0, x1, os in zip(x[:-1], x[1:], osc):
if os:
plt.axvspan(x0, x1, color='blue', alpha=0.2, lw=0)
plt.margins(x=0)
plt.show()
Note that only the first 99 values of osc are used, as there are only 99 intervals.
See code below:
import numpy as np
import matplotlib.pyplot as plt
n = 100
x = range(n)
y = np.random.rand(100)
osc = np.random.randint(2, size=n)
fig,ax = plt.subplots()
ax.plot(x,y)
ax.axvspan(0,5,facecolor='grey', alpha=0.4)
plt.show()
Documentation on axvspan can be found here: https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.axvspan.html.
Similarly you can use axvline for just vertical lines.
Why I am getting a runtime error while running this code?? I wanted to show two SST data in a single plot using for loop. while I am running the code it shows your system has crashed. I am sharing the screenshot of the system log.
Screenshot of the error
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.basemap import Basemap
from netCDF4 import Dataset
from mpl_toolkits.axes_grid1 import ImageGrid
def read_sst (x):
ncfile=x
fh=Dataset(ncfile,mode='r')
sst=fh.variables['sst'][:]
sst_anom=sst[0,:,:]
return sst_anom
sst_anom_drought=read_sst("Drought_SST_Mean_Anomaly_Composite_1901-2014.nc")
sst_anom_flood=read_sst("Flood_SST_Mean_Anomaly_Composite_1901-2014.nc")
SST_ncfile='Flood_SST_Mean_Anomaly_Composite_1901-2014.nc'
fh1=Dataset(SST_ncfile,mode='r')
lons = fh1.variables['longitude'][:]
lats = fh1.variables['latitude'][:]
sst_data=[sst_anom_drought,sst_anom_flood]
fig = plt.figure(1,(15.,5.))
grid_top = ImageGrid(fig, 111, nrows_ncols = (2, 1),axes_pad=0.5)
cbar_ax = fig.add_axes([0.25,0.15, 0.55, 0.015])
for g, s in zip(grid_top,sst_data):
plt.sca(g)
m = Basemap(resolution='l',projection='merc',llcrnrlon=30, llcrnrlat=-31,
urcrnrlon=360, urcrnrlat=65)
clevs = np.linspace(-0.8, 0.8, 15)
lons, lats = np.meshgrid(lons, lats)
xi, yi = m(lons, lats)
color_map = plt.cm.RdBu_r
#reversed_color_map = color_map.reversed()
cs = m.contourf(xi,yi,s,clevs,cmap=color_map,extend='both')
cb=fig.colorbar(cs,orientation="horizontal",cax=cbar_ax)
m.drawparallels(np.arange(-30., 65., 15.), labels=[1,0,0,0], fontsize=12,linewidth=0.1)
m.drawmeridians(np.arange(-180., 180., 40.), labels=[0,0,0,1], fontsize=12,linewidth=0.1)
m.fillcontinents(color='whitesmoke',lake_color='whitesmoke')
m.drawcoastlines()
I want to fill the gridded map with colors based on the value of interest. A sample data is here:
import pandas as pd
df = pd.DataFrame()
df['lon'] = [100,105,110,115,120,125,130]
df['lat'] = [38,40,42,44,46,48,50]
df['value'] = [1,2,3,4,5,6,7]
Specifically, is it possible to do this with Cartopy? I found a similar question here:https://stackoverflow.com/questions/53412785/plotting-pandas-csv-data-onto-cartopy-map. But that post was to plot scattered points, I need to fill the grids with colors.
I myself tried:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import cartopy.crs as ccrs
lon, lat = np.meshgrid(df['lon'], df['lat'])
fig = plt.figure(figsize=[15,15])
ax = plt.axes(projection=ccrs.PlateCarree())
ax.pcolormesh(lon,lat,df['variable'],latlon=True,cmap='jet')
plt.show()
The error is at ax.pcolormesh(...), it says "not enough values to unpack (expected 2, got 1)"
Many thanks for your help.
For discrete data you can create rectangular patches for each point. Here is a possible solution for your sample data set. Each row of data (lat, long, value) is used to create a rectangular patch. The value is normalized by dividing with max(value) to enable using colormap for coloring the patches.
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import cartopy.crs as ccrs
import matplotlib.patches as mpatches
def make_rect(clon, clat, dlon, dlat):
lon_min = clon - dlon/2.
lat_min = clat - dlat/2.
lon_max = clon + dlon/2.
lat_max = clat + dlat/2.
# clockwise from LL
#lons = [lon_min, lon_min, lon_max, lon_max, lon_min]
#lats = [lat_min, lat_max, lat_max, lat_min, lat_min]
ll = [lon_min,lat_min]
ul = [lon_min,lat_max]
ur = [lon_max,lat_max]
lr = [lon_max,lat_min]
return [ll, ul, ur, lr, ll]
df = pd.DataFrame()
df['lon'] = [100,105,110,115,120,125,130]
df['lat'] = [38,40,42,44,46,48,50]
df['value'] = [1,2,3,4,5,6,7] # not suffice for meshgrid plot
# The colormap to use.
cm = plt.cm.get_cmap('jet')
fig = plt.figure(figsize=[8,6])
ax = plt.axes(projection=ccrs.PlateCarree(), extent=[95, 134, 35, 52])
# plot the red dots using the available data
# comment out if not needed
ax.plot(df['lon'], df['lat'], 'ro')
# plot rectangular patches at the data points
dlon, dlat = 5, 2 #spacings between data points
for lon1, lat1, val1 in zip(df['lon'], df['lat'], df['value']):
pcorners = make_rect(lon1, lat1, dlon, dlat)
poly = mpatches.Polygon(pcorners, ec='gray', fill=True, lw=0.25, \
fc=cm(val1 / max(df['value'])), transform=ccrs.PlateCarree())
ax.add_patch(poly)
ax.gridlines(draw_labels=True)
plt.show()
The output plot: