Creating US map with 50 state density and color bar using basemap - matplotlib

I have a dictionary named density, I am trying to create a US state map as the color shows the density of the state. I am trying to replicate this use Basemap (Python) to plot US with 50 states
however I am getting error.
This is my data:
density = {'NY': 648.0,
'FL': 696.0,
'TX': 833.0,
'CA': 927.0,
'PA': 472.0,
'OH': 721.0,
'NJ': 645.0,
'IL': 607.0,
'MI': 570.0,
'AZ': 616.0,
'GA': 799.0,
'MD': 652.0,
'NC': 720.0,
'LA': 546.0,
'TN': 806.0,
'MO': 564.0,
'SC': 574.0,
'VA': 818.0,
'IN': 780.0,
'AL': 619.0,
'MA': 626.0,
'WA': 749.0,
'KY': 680.0,
'WI': 615.0,
'OK': 633.0,
'MN': 743.0,
'IA': 543.0,
'WV': 599.0,
'MS': 695.0,
'AR': 698.0,
'OR': 878.0,
'CO': 782.0,
'NV': 930.0,
'KS': 637.0,
'CT': 1078.0,
'UT': 580.0,
'NM': 667.0,
'NE': 552.0,
'PR': 698.0,
'ME': 702.0,
'ID': 679.0,
'DE': 845.0,
'NH': 668.0,
'RI': 616.0,
'HI': 1131.0,
'DC': 711.0,
'MT': 653.0,
'SD': 495.0,
'ND': 685.0,
'VT': 754.0,
'AK': 1080.0,
'WY': 1028.0,
'VI': 1261.0,
'GU': 889.0}
Here is my code which I get the error.
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.basemap import Basemap as Basemap
from matplotlib.colors import rgb2hex
from matplotlib.patches import Polygon
m = Basemap(llcrnrlon=-119,llcrnrlat=22,urcrnrlon=-64,urcrnrlat=49,
projection='lcc',lat_1=33,lat_2=45,lon_0=-95)
shp_info = m.readshapefile('st99_d00','states',drawbounds=True)
colors={}
statenames=[]
cmap = plt.cm.hot # use 'hot' colormap
vmin = 0; vmax = 450 # set range.
for shapedict in m.states_info:
statename = shapedict['NAME']
if statename not in ['District of Columbia','Puerto Rico']:
pop = popdensity[statename]
colors[statename] = cmap(1.-np.sqrt((pop-vmin)/(vmax-vmin)))[:3]
statenames.append(statename)
ax = plt.gca() # get current axes instance
for nshape,seg in enumerate(m.states):
if statenames[nshape] not in ['Puerto Rico', 'District of Columbia']:
if statenames[nshape] == 'Alaska':
seg = list(map(lambda (x,y): (0.35*x + 1100000, 0.35*y-1300000), seg))
if statenames[nshape] == 'Hawaii':
seg = list(map(lambda (x,y): (x + 5100000, y-900000), seg))
color = rgb2hex(colors[statenames[nshape]])
poly = Polygon(seg,facecolor=color,edgecolor=color)
ax.add_patch(poly)
plt.title('******')
plt.show()
I am confused what I need to do to this code work.
I am new to pyhton, any help and feedback is highly appreciated.
TIA!

Related

Flight Path by shapely LineString is not correct

I want to connect airplanes in origin (lat_1 lon_1) to dest(lat_2 lon_2). I use these data.
callsign
latitude_1
longitude_1
latitude_2
longitude_2
0
HBAL102
-4.82114
-76.3194
-4.5249
-79.0103
1
AUA1028
-33.9635
151.181
48.1174
16.55
2
ABW120
41.9659
-87.8832
55.9835
37.4958
3
CSN461
33.9363
-118.414
50.0357
8.5723
4
ETH3730
25.3864
55.4221
50.6342
5.43903
But unfortunately, I would get an incorrect result when creating LineString with shapely. I used everything like rotate and affine but it didn't correct.
Code:
cols = pd.read_csv("/content/dirct_lines.csv",sep=";")
line = cols[["callsign","latitude_1","longitude_1","latitude_2","longitude_2"]].dropna()
line['geometry'] = line.apply(lambda x: [(x['latitude_1'],
x['longitude_1']),
(x['latitude_2'],
x['longitude_2'])], axis = 1)
geoline = gpd.GeoDataFrame(line,geometry="geometry",
crs="EPSG:4326")
import matplotlib.pyplot as plt
world = gpd.read_file(gpd.datasets.get_path('naturalearth_lowres'))
ax = world.plot(figsize=(14,9),
color='white', edgecolor='black')
geoline.plot(figsize=(14,9),ax=ax,facecolor = 'lightgrey', linewidth = 1.75,
edgecolor = 'red',
alpha = 2)
plt.show()
Shapely Output:
something that was interesting for me was that when I use Matplotlib to create lines everything is correct.
Code:
import cartopy.crs as ccrs
import matplotlib.pyplot as plt
fig = plt.figure(figsize=(12, 8))
ax = fig.add_subplot(projection=ccrs.PlateCarree())
ax.stock_img()
org_lon, org_lat = cols["longitude_1"], cols["latitude_1"]
dst_lon, dst_lat = cols["longitude_2"], cols["latitude_2"]
plt.plot([org_lon, dst_lon], [org_lat, dst_lat],
color='black', linewidth=0.5, marker='_',
transform=ccrs.PlateCarree()
)
plt.savefig(f"fight_path.png",dpi=60,facecolor = None, bbox_inches = 'tight', pad_inches = None)
plt.show()
Matplotlib Output:
What is the problem?
why isn't correct by shapely?
it's just the way you are creating the geometry. Below works correctly.
import io
import geopandas as gpd
import pandas as pd
import shapely.geometry
df = pd.read_csv(
io.StringIO(
"""callsign,latitude_1,longitude_1,latitude_2,longitude_2
HBAL102,-4.82114,-76.3194,-4.5249,-79.0103
AUA1028,-33.9635,151.181,48.1174,16.55
ABW120,41.9659,-87.8832,55.9835,37.4958
CSN461,33.9363,-118.414,50.0357,8.5723
ETH3730,25.3864,55.4221,50.6342,5.43903
"""
)
)
geoline = gpd.GeoDataFrame(
geometry=[
shapely.geometry.LineString(points)
for points in zip(
gpd.points_from_xy(df["longitude_1"], df["latitude_1"]),
gpd.points_from_xy(df["longitude_2"], df["latitude_2"]),
)
],
data=df,
)
import matplotlib.pyplot as plt
world = gpd.read_file(gpd.datasets.get_path("naturalearth_lowres"))
ax = world.plot(figsize=(14, 9), color="white", edgecolor="black")
geoline.plot(
figsize=(14, 9),
ax=ax,
facecolor="lightgrey",
linewidth=1.75,
edgecolor="red",
)
plt.show()

LineCollections for few lines with a single colorbar

I'm trying to plot some lines using LineCollection in a plot. Each of these lines is needed to be mapped to colorbar whose range varies for each lines. I tried as explained here
https://matplotlib.org/stable/gallery/lines_bars_and_markers/multicolored_line.html?highlight=line%20collection
In the end, I want a single colorbar, for let's say three lines, covering all ranges. However, the colorbar is set for the last line values. So I looked here
https://matplotlib.org/stable/gallery/images_contours_and_fields/multi_image.html
But I'm not being successful since I'm quite new to Matplotlib. I paste my code below. I'm just trying to map the value of the lines (also shown on y-axis) in a colorbar for all three lines. Any help is appreciated.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.collections import LineCollection
from matplotlib import colors
lineSegments = [np.linspace(0,1,10),
np.linspace(0,5,10),
np.linspace(0,2,10)]
xVec = np.linspace(0,1,10)
fig, ax = plt.subplots()
for i in range(0, len(lineSegments)):
cValue = np.linspace( min(lineSegments[i]), max(lineSegments[i]) )
norm = colors.Normalize(vmin=cValue.min(), vmax=cValue.max() )
Points = np.array([xVec, lineSegments[i]]).T.reshape(-1,1,2)
PointSegments = np.concatenate([Points[:-1],Points[1:]], axis=1)
lc = LineCollection(PointSegments, cmap=plt.get_cmap('jet'),
norm=norm)
#plt.gca().add_collection(lc)
ax.add_collection(lc)
ax.set_xlim( min(xVec), max(xVec) )
ax.set_ylim( np.amin(lineSegments), np.amax(lineSegments) )
lc.set_array(cValue)
fig.colorbar(lc)
def update(changed_lines):
for i in range(0, len(lineSegments)):
if (changed_lines.get_cmap() != lc.get_cmap()
or changed_lines.get_clim() != lc.get_clim()):
lc.set_cmap(changed_lines.get_cmap())
lc.set_clim(changed_lines.get_clim())
for i in range(0, len(lineSegments)):
lc.callbacksSM.connect('changed',update)
plt.show()
I have modified your code. Essentially what you need to do is create a norm instance for the entire dataset and then assign color values to the segments according to the colormap you have with the given norm. You can then pass it to the colorbar accordingly.
As such
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.collections import LineCollection
from matplotlib import colors
lineSegments = [np.linspace(0,1,10),
np.linspace(0,5,10),
np.linspace(0,2,10)]
xVec = np.linspace(0,1,10)
fig, ax = plt.subplots()
norm = colors.Normalize(vmin=min([ i.min() for i in lineSegments ]),
vmax=max([i.max() for i in lineSegments]))
cmap = plt.get_cmap('jet')
for i in range(0, len(lineSegments)):
cValue = norm(lineSegments[i])
c = cmap(cValue)
Points = np.array([xVec, lineSegments[i]]).T.reshape(-1,1,2)
PointSegments = np.concatenate([Points[:-1],Points[1:]], axis=1)
lc = LineCollection(PointSegments, cmap=cmap,
norm=norm, colors = c)
#plt.gca().add_collection(lc)
ax.add_collection(lc)
ax.set_xlim( min(xVec), max(xVec) )
ax.set_ylim( np.amin(lineSegments), np.amax(lineSegments) )
# lc.set_array(cValue)
sc = plt.cm.ScalarMappable(norm = norm, cmap = cmap)
fig.colorbar(sc)
def update(changed_lines):
for i in range(0, len(lineSegments)):
if (changed_lines.get_cmap() != lc.get_cmap()
or changed_lines.get_clim() != lc.get_clim()):
lc.set_cmap(changed_lines.get_cmap())
lc.set_clim(changed_lines.get_clim())
for i in range(0, len(lineSegments)):
lc.callbacksSM.connect('changed',update)
plt.show()

pulling data out of bins in density map created with matplotlib

I have created a lightning density map using lines of data representing lightning strikes. One line is shown below:
1996-01-17 03:54:35.853 44.9628 -78.9399 -37.9
Now that I have applied these lines of data to the density map and distributed them into their appropriate bins based on Lat/Long, I would like to pull the data back out specific to the bin that it fell into so that I can manipulate that data further.
I have tried to find answers to this online but have failed to find anything that is specific to what I am trying to do. Any and all help is greatly appreciated!
my code:
import matplotlib.pyplot as plt
import matplotlib as mpl
import matplotlib.axes as ax
import numpy as np
import cartopy.crs as ccrs
import cartopy.feature as cfeature
from metpy.plots import USCOUNTIES
from matplotlib.axes import Axes
from cartopy.mpl.geoaxes import GeoAxes
GeoAxes._pcolormesh_patched = Axes.pcolormesh
import datetime
fig, ax = plt.subplots(figsize=(15,15),subplot_kw=dict(projection=ccrs.Stereographic(central_longitude=-76, central_latitude=43)))
ax.set_extent([-79, -73, 42, 45],ccrs.Geodetic())
ax.add_feature(USCOUNTIES.with_scale('500k'), edgecolor='gray', linewidth=0.25)
ax.add_feature(cfeature.STATES.with_scale('50m'))
winter = [12, 1, 2]
summer = [6, 7, 8]
seasondata = []
lons=[]
lats=[]
f = open("2007-2016.txt", "r")
for line in f.readlines():
parts = line.split()
dates = parts[0]
charges = float(parts[4])
date = datetime.datetime.strptime(dates, "%Y-%m-%d")
#if date.month in summer:
if date.month in winter:
seasondata.append(line)
if charges <= 0:
seasondata.append(line)
lon = float(parts[3])
lat = float(parts[2])
lons.append(lon)
lats.append(lat)
if charges >= 15:
seasondata.append(line)
lon = float(parts[3])
lat = float(parts[2])
lons.append(lon)
lats.append(lat)
lons=np.array(lons)
lats=np.array(lats)
ax.set_title('2007-2016 Jan, Feb, Dec: Lightning Density', loc ='Left')
xynps = (ax.projection.transform_points(ccrs.Geodetic(), lons, lats))
bins=[300,240]
h2d, xedges, yedges, im = ax.hist2d(xynps[:,0], xynps[:,1], bins=bins, cmap=plt.cm.YlOrRd, zorder=10, alpha=0.4)
lons=[]
lats=[]
f = open("turbine.txt", "r")
for line in f.readlines():
parts = line.split()
lat=float(parts[0])
lon=float(parts[1])
lats.append(lat)
lons.append(lon)
markerSymbol='o'
markerSize=10
ax.scatter(lons, lats, transform=ccrs.PlateCarree(), marker = markerSymbol, s=markerSize, c='b')
cbar = plt.colorbar(im, fraction=0.046, pad=0.04)
cbar.set_label("Flashes per 2km^2")
plt.show()

How do I animate a circle to move horizontally?

import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
import numpy as np
xvalues = np.arange(4000000, 6000000+1000, 1000).tolist()
yvalues = [5000000]*2001
Acc_11 = xvalues
Acc_12 = yvalues
fig = plt.figure(figsize = (5,5))
axes = fig.add_subplot(111)
axes.set_xlim((0, 10000000))
axes.set_ylim((0, 10000000))
point, = plt.Circle((4000000, 5000000), 60000, color = "black")
def ani(coords):
point.set_data([coords[0]],[coords[1]])
return point
def frames():
for acc_11_pos, acc_12_pos in zip(Acc_11, Acc_12):
yield acc_11_pos, acc_12_pos
ani = FuncAnimation(fig, ani, frames=frames, interval=10)
plt.show()
Im getting TypeError: 'Circle' object is not iterable. What I need to do? The size of a circle must be changable and related to axes, so matplotlib circle is the only option (I guess).
Here's a possible solution (assuming you are running in a jupyter notebook cell):
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
from IPython.display import HTML
xvalues = np.arange(4000000, 6000000+1000, 1000).tolist()
yvalues = [5000000]*2001
Acc_11 = xvalues
Acc_12 = yvalues
fig = plt.figure(figsize = (5,5))
axes = fig.add_subplot(111)
axes.set_xlim((0, 10000000))
axes.set_ylim((0, 10000000))
point = plt.Circle((4000000, 5000000), 60000, color = "black")
def init():
point.center = (5, 5)
axes.add_patch(point)
return point,
def ani(i):
point.center = (Acc_11[i],Acc_12[i])
return point
anim = FuncAnimation(fig,
ani,
init_func=init,
frames=200, #len(Acc_11),
interval=10)
HTML(anim.to_html5_video())
You may want to change frames=200 to frames=len(Acc_11) but it will take a while to run.

Adding Labels to a Shapefile Map

I have a shapefile that maps the world to sales territories. The shapefile records lists the sales territory code and name. I would like to be able to add the territory code in the center of the region, but to do using ax.text, I need the center point of the region. Any ideas how to do this?
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
import shapefile
from mpl_toolkits.basemap import Basemap as Basemap
from matplotlib.colors import rgb2hex, Normalize
from matplotlib.patches import Polygon
from matplotlib.colorbar import ColorbarBase
from matplotlib.collections import PatchCollection
plt.rcParams['figure.figsize'] = [16,12]
fig = plt.figure()
m = Basemap(llcrnrlon=-121,llcrnrlat=20,urcrnrlon=-62,urcrnrlat=51,
projection='lcc',lat_1=32,lat_2=45,lon_0=-95)
shp_info = m.readshapefile('world_countries_boundary_file_world_2002','countries',drawbounds=True)
sf = shapefile.Reader('territory_map') # Custom file mapping out territories
recs = sf.records()
shapes = sf.shapes()
Nshp = len(shapes)
colors={}
territory_codes=[]
cmap = plt.cm.RdYlGn
# details is a pandas datafile with column "DELTA" that has data to plot
vmin = details.DELTA.min()
vmax = details.DELTA.max()
norm = Normalize(vmin=vmin, vmax=vmax)
for index,row in details.iterrows():
colors[row['TERRITORY_CODE']] = cmap((row['DELTA']-vmin)/(vmax-vmin))[:3]
territory_codes.append(row['TERRITORY_CODE'])
ax = fig.add_subplot(111)
for nshp in range(Nshp):
ptchs = []
pts = np.array((shapes[nshp].points))
prt = shapes[nshp].parts
par = list(prt) + [pts.shape[0]]
for pij in range(len(prt)):
ptchs.append(Polygon(pts[par[pij]:par[pij+1]]))
try:
color = rgb2hex(colors[recs[nshp][0]])
except:
color = 'w' # If no data, leave white (blank)
ax.add_collection(PatchCollection(ptchs, facecolor=color, edgecolor='b', linewidths=.7))
x, y = # Coordinates that are center of region
ax.text(x, y, recs[nshp][0]) # <---- this would be the text to add
# Add colorbar
ax_c = fig.add_axes([0.1, 0.1, 0.8, 0.02])
cb = ColorbarBase(ax_c,cmap=cmap,norm=norm,orientation='horizontal')
cb.ax.set_xlabel('Daily Change, USD')
#Set view to United States
ax.set_xlim(-150,-40)
ax.set_ylim(15,70)
plt.show()
Resulting Map of Code without Territory Names
you're probably looking to take the mean of all the x coordinates and the mean of all the y coordinates of your polygon shape.
I can't test this but it could look something like this:
x,y = pts[0].mean(), pts[1].mean()
or this:
x,y = pts[:,0].mean(), pts[:,1].mean()
depending on the dimensions of your numpy array.