How can we can give names to each states on the basemap? - matplotlib

Can any help me out how can we give names to each states in the map itself. I am able plot the data points but not able name the sates as i am using the shapefile. If would have been plotting totally through the lats and longs i would have taken the mean of each state and used plt.text(x.y,statename) but i dont know with shapefiles..
Below is the python code using matplotlib library.....?
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
# Lambert Conformal map of lower 48 states.
fig=plt.figure(figsize=(15,9))
m = Basemap(llcrnrlon=-119,llcrnrlat=22,urcrnrlon=-64,urcrnrlat=49,
projection='lcc',lat_1=33,lat_2=45,lon_0=-95,resolution='c')
# draw state boundaries.
# data from U.S Census Bureau
# http://www.census.gov/geo/www/cob/st2000.html
shp_info = m.readshapefile('cb_2018_us_state_20m','states',drawbounds=True)
# population density by state from
# http://en.wikipedia.org/wiki/List_of_U.S._states_by_population_density
popdensity = {
'New Jersey': 438.00,
'Rhode Island': 387.35,
'Massachusetts': 312.68,
'Connecticut': 271.40,
'Maryland': 209.23,
'New York': 155.18,
'Delaware': 154.87,
'Florida': 114.43,
'Ohio': 107.05,
'Pennsylvania': 105.80,
'Illinois': 86.27,
'California': 83.85,
'Hawaii': 72.83,
'Virginia': 69.03,
'Michigan': 67.55,
'Indiana': 65.46,
'North Carolina': 63.80,
'Georgia': 54.59,
'Tennessee': 53.29,
'New Hampshire': 53.20,
'South Carolina': 51.45,
'Louisiana': 39.61,
'Kentucky': 39.28,
'Wisconsin': 38.13,
'Washington': 34.20,
'Alabama': 33.84,
'Missouri': 31.36,
'Texas': 30.75,
'West Virginia': 29.00,
'Vermont': 25.41,
'Minnesota': 23.86,
'Mississippi': 23.42,
'Iowa': 20.22,
'Arkansas': 19.82,
'Oklahoma': 19.40,
'Arizona': 17.43,
'Colorado': 16.01,
'Maine': 15.95,
'Oregon': 13.76,
'Kansas': 12.69,
'Utah': 10.50,
'Nebraska': 8.60,
'Nevada': 7.03,
'Idaho': 6.04,
'New Mexico': 5.79,
'South Dakota': 3.84,
'North Dakota': 3.59,
'Montana': 2.39,
'Wyoming': 1.96,
'Alaska': 0.42}
# choose a color for each state based on population density.
colors={}
statenames=[]
cmap = plt.cm.hot # use 'hot' colormap
vmin = 0; vmax = 450 # set range.
for shapedict in m.states_info:
statename = shapedict['NAME']
# skip DC and Puerto Rico.
if statename not in ['District of Columbia','Puerto Rico']:
pop = popdensity[statename]
# calling colormap with value between 0 and 1 returns
# rgba value. Invert color range (hot colors are high
# population), take sqrt root to spread out colors more.
colors[statename] = cmap(1.-np.sqrt((pop-vmin)/(vmax-vmin)))[:3]
statenames.append(statename)
# cycle through state names, color each one.
ax = plt.gca() # get current axes instance
for nshape,seg in enumerate(m.states):
#skip DC and Puerto Rico.
if statenames[nshape] not in ['District of Columbia','Puerto Rico']:
color = rgb2hex(colors[statenames[nshape]])
poly = Polygon(seg,facecolor=color,edgecolor=color)
plt.text()
ax.add_patch(poly)
plt.title('Filling States with Density of Merchants')
x, y = m(-80.2416355,37.7652076 )
plt.plot(x, y, 'ok', markersize=10)
plt.show()

Related

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

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!

Matplotlib Legend in For Loop

Im am trying to plot multiple lines with their corresponding legend:
regions = ['Wales', 'Scotland', 'London', 'East of England', 'East Midlands',
'Yorkshire and The Humber', 'South East', 'South West',
'West Midlands', 'North West', 'North East']
plt.figure(figsize = (10,8))
plt.title('Number of Vehicles per Region')
plt.xlabel('Year')
plt.ylabel('Number of Vehicles')
plt.legend()
for i in regions:
region = raw_miles_df.loc[i].sum(axis = 1).reset_index()
region = region.rename(columns = {'count_date':'Year', 0: 'vehicles'})
region['Year'] = region['Year'].apply(lambda x: x.year)
region = region.groupby(['Year']).agg(vehicles = ('vehicles', lambda x: x.mean().round(2)))
plt.plot(region)
plt.legend(i)
the method i have is not working:
You need to move plt.legend out of the loop and make it plt.legend(regions). As you can see in the legend, it is treating the string 'North East', which is the last item in regions, as an iterable from which to draw the categories.
But you can make it easier on yourself by using seaborn
import seaborn as sns
# aggregate your data outside of the loop
# then call lineplot
aggdata = df.groupby(...)
sns.lineplot(x=x_column, y=y_column, hue=category_column, data=aggdata)

Using Seaborn's relplot instead of Matplotlib's plot

I have a line graph built using matplotlib with the following code:
f = plt.figure(figsize=(20, 7))
sns.set_style("darkgrid")
ax = plt.subplot(111)
df.plot(x='Date', y=['Burglary',
'Criminal Damage',
'Criminal Damage',
'Drugs',
'Fraud or Forgery',
'Robbery',
'Sexual Offences',
'Theft and Handling',
'Violence Against The Person',
'Other Notifiable Offences'], ax=f.gca())
ax.legend(loc='center left', bbox_to_anchor=(1, 0.5))
plt.show()
It works fine. df.head() looks like:
When I try to use relplot from Seaborn, I get the following error:
ValueError: could not broadcast input array from shape (10) into shape (105)
Modified code is as follows:
f = plt.figure(figsize=(20, 7))
sns.set_style("darkgrid")
ax = plt.subplot(111)
sns.relplot(data=df, x='Date', y=['Burglary',
'Criminal Damage',
'Criminal Damage',
'Drugs',
'Fraud or Forgery',
'Robbery',
'Sexual Offences',
'Theft and Handling',
'Violence Against The Person',
'Other Notifiable Offences'], ax=f.gca())
ax.legend(loc='center left', bbox_to_anchor=(1, 0.5))
plt.show()
Does the dataframe need to be in some different structure or am I missing something in the code? I want to use Seaborn so I can turn on or off the different categories so the graph is more readable when presenting.
Thanks in advance.

Showing Alaska and Hawaii in Cartopy map

The following code creates a map of continental US states that is shaded by population density. I want to create a similar map (my data is not actually pop density, but this is an easy example), except that it also includes the states of Alaska and Hawaii.
Specifically I would like to have Alaska/Hawaii show up in the figure, but be moved so that they are below the part of the figure showing the continental US. Or something along those lines.
Any idea how I would create such a map using Cartopy?
import matplotlib.pyplot as plt
import cartopy.crs as ccrs
import cartopy.io.shapereader as shpreader
fig = plt.figure()
ax = fig.add_axes([0, 0, 1, 1], projection=ccrs.LambertConformal())
ax.set_extent([-125, -66.5, 20, 50], ccrs.Geodetic())
shapename = 'admin_1_states_provinces_lakes_shp'
states_shp = shpreader.natural_earth(resolution='110m',
category='cultural', name=shapename)
popdensity = {
'New Jersey': 438.00,
'Rhode Island': 387.35,
'Massachusetts': 312.68,
'Connecticut': 271.40,
'Maryland': 209.23,
'New York': 155.18,
'Delaware': 154.87,
'Florida': 114.43,
'Ohio': 107.05,
'Pennsylvania': 105.80,
'Illinois': 86.27,
'California': 83.85,
'Virginia': 69.03,
'Michigan': 67.55,
'Indiana': 65.46,
'North Carolina': 63.80,
'Georgia': 54.59,
'Tennessee': 53.29,
'New Hampshire': 53.20,
'South Carolina': 51.45,
'Louisiana': 39.61,
'Kentucky': 39.28,
'Wisconsin': 38.13,
'Washington': 34.20,
'Alabama': 33.84,
'Missouri': 31.36,
'Texas': 30.75,
'West Virginia': 29.00,
'Vermont': 25.41,
'Minnesota': 23.86,
'Mississippi': 23.42,
'Iowa': 20.22,
'Arkansas': 19.82,
'Oklahoma': 19.40,
'Arizona': 17.43,
'Colorado': 16.01,
'Maine': 15.95,
'Oregon': 13.76,
'Kansas': 12.69,
'Utah': 10.50,
'Nebraska': 8.60,
'Nevada': 7.03,
'Idaho': 6.04,
'New Mexico': 5.79,
'South Dakota': 3.84,
'North Dakota': 3.59,
'Montana': 2.39,
'Wyoming': 1.96}
ax.background_patch.set_visible(False)
ax.outline_patch.set_visible(False)
ax.set_title('State Population Density')
for state in shpreader.Reader(states_shp).records():
edgecolor = 'black'
try:
# use the name of this state to get pop_density
state_dens = popdensity[ state.attributes['name'] ]
except:
state_dens = 0
# simple scheme to assign color to each state
if state_dens < 40:
facecolor = "lightyellow"
elif state_dens > 200:
facecolor = "red"
else:
facecolor = "pink"
# `state.geometry` is the polygon to plot
ax.add_geometries([state.geometry], ccrs.PlateCarree(),
facecolor=facecolor, edgecolor=edgecolor)
plt.show()
The figure this (currently) creates is as follows:
To plot inset maps as parts of a main map is challenging. You will need to create an axes for plotting each inset map and place it on the figure at proper location and relative scale. Here is a working code that you can experiment with.
import matplotlib.pyplot as plt
import cartopy
import cartopy.crs as ccrs
import cartopy.io.shapereader as shpreader
import shapely.geometry as sgeom
# A function that draws inset map, ++
# ===================================
def add_insetmap(axes_extent, map_extent, state_name, facecolor, edgecolor, geometry):
# create new axes, set its projection
use_projection = ccrs.Mercator() # preserve shape well
#use_projection = ccrs.PlateCarree() # large distortion in E-W for Alaska
geodetic = ccrs.Geodetic(globe=ccrs.Globe(datum='WGS84'))
sub_ax = plt.axes(axes_extent, projection=use_projection) # normal units
sub_ax.set_extent(map_extent, geodetic) # map extents
# add basic land, coastlines of the map
# you may comment out if you don't need them
sub_ax.add_feature(cartopy.feature.LAND)
sub_ax.coastlines()
sub_ax.set_title(state_name)
# add map `geometry` here
sub_ax.add_geometries([geometry], ccrs.PlateCarree(), \
facecolor=facecolor, edgecolor=edgecolor)
# +++ more features can be added here +++
# plot box around the map
extent_box = sgeom.box(map_extent[0], map_extent[2], map_extent[1], map_extent[3])
sub_ax.add_geometries([extent_box], ccrs.PlateCarree(), color='none', linewidth=0.05)
fig = plt.figure()
ax = fig.add_axes([0, 0, 1, 1], projection=ccrs.LambertConformal())
ax.set_extent([-125, -66.5, 20, 50], ccrs.Geodetic())
shapename = 'admin_1_states_provinces_lakes_shp'
states_shp = shpreader.natural_earth(resolution='110m',
category='cultural', name=shapename)
popdensity = {
'New Jersey': 438.00,
'Rhode Island': 387.35,
'Massachusetts': 312.68,
'Connecticut': 271.40,
'Maryland': 209.23,
'New York': 155.18,
'Delaware': 154.87,
'Florida': 114.43,
'Ohio': 107.05,
'Pennsylvania': 105.80,
'Illinois': 86.27,
'California': 83.85,
'Virginia': 69.03,
'Michigan': 67.55,
'Indiana': 65.46,
'North Carolina': 63.80,
'Georgia': 54.59,
'Tennessee': 53.29,
'New Hampshire': 53.20,
'South Carolina': 51.45,
'Louisiana': 39.61,
'Kentucky': 39.28,
'Wisconsin': 38.13,
'Washington': 34.20,
'Alabama': 33.84,
'Missouri': 31.36,
'Texas': 30.75,
'West Virginia': 29.00,
'Vermont': 25.41,
'Minnesota': 23.86,
'Mississippi': 23.42,
'Iowa': 20.22,
'Arkansas': 19.82,
'Oklahoma': 19.40,
'Arizona': 17.43,
'Colorado': 16.01,
'Maine': 15.95,
'Oregon': 13.76,
'Kansas': 12.69,
'Utah': 10.50,
'Nebraska': 8.60,
'Nevada': 7.03,
'Idaho': 6.04,
'New Mexico': 5.79,
'South Dakota': 3.84,
'North Dakota': 3.59,
'Montana': 2.39,
'Wyoming': 1.96}
ax.background_patch.set_visible(False)
ax.outline_patch.set_visible(False)
ax.set_title('State Population Density')
for state in shpreader.Reader(states_shp).records():
edgecolor = 'black'
try:
# use the name of this state to get pop_density
state_dens = popdensity[ state.attributes['name'] ]
except:
state_dens = 0
# simple scheme to assign color to each state
if state_dens < 40:
facecolor = "lightyellow"
elif state_dens > 200:
facecolor = "red"
else:
facecolor = "pink"
# special handling for the 2 states
# ---------------------------------
if state.attributes['name'] in ("Alaska", "Hawaii"):
# print("state.attributes['name']:", state.attributes['name'])
state_name = state.attributes['name']
# prep map settings
# experiment with the numbers in both `_extents` for your best results
if state_name == "Alaska":
# (1) Alaska
map_extent = (-178, -135, 46, 73) # degrees: (lonmin,lonmax,latmin,latmax)
axes_extent = (0.04, 0.06, 0.29, 0.275) # axes units: 0 to 1, (LLx,LLy,width,height)
if state_name == "Hawaii":
# (2) Hawii
map_extent = (-162, -152, 15, 25)
axes_extent = (0.27, 0.06, 0.15, 0.15)
# add inset maps
add_insetmap(axes_extent, map_extent, state_name, \
facecolor, \
edgecolor, \
state.geometry)
# the other (conterminous) states go here
else:
# `state.geometry` is the polygon to plot
ax.add_geometries([state.geometry], ccrs.PlateCarree(),
facecolor=facecolor, edgecolor=edgecolor)
plt.show()
The output plot will be:

Cartopy - multiple arrows using annotate

Using this solution as base, is it possible to create multiple arrows emanating from the same source to different targets? e.g. Delhi -> Beijing (116.4, 39.9), Delhi -> Cairo (30.0, 31.2), Delhi -> Tokyo (35.6, 139.6)?
When I repeat the code below, I only get the first arrow.
#Dehli - Beijing
ax.annotate('Beijing', xy=(116.4, 39.9), xycoords=transform,
size=40,
)
ax.annotate('Delhi', xy=(113, 40.5), xytext=(77.23, 28.61),
size=40,
arrowprops=dict(facecolor='red', ec = 'none',
arrowstyle="fancy",
connectionstyle="arc3,rad=-0.2",
),
xycoords=transform,
)
#Dehli - Cairo
ax.annotate('Cairo', xy=(-6.26, 53.34), xycoords=transform,
size=40,
)
ax.annotate('Delhi', xy=(113, 40.5), xytext=(77.23, 28.61),
size=40,
arrowprops=dict(facecolor='red', ec = 'none',
arrowstyle="fancy",
connectionstyle="arc3,rad=-0.2",
),
xycoords=transform,
)
Alternatively, is there a way to put .annotate into this expression which I'm using at present to draw connecting lines. I've tried to no avail:
#Coordinates
lon_dehl, lat_dehl = 116.4, 39.9
lon_beij, lat_beij = 77.59, 12.97
lon_toky, lat_toky = 35.6, 139.6
lon_cair, lat_cair = 30.0, 31.2
plt.plot([lon_dehl, lon_beij], [lat_dehl, lat_beij],
linewidth=2,
linestyle='solid',
solid_capstyle='round',
color='#cb2c31',
marker='o',
markersize=6,
markeredgewidth=None,
markeredgecolor='#cb2c31',
transform=ccrs.PlateCarree(),
)
This isn't perfect (in fact, I'd welcome any improvements), but I achieved multiple arrows with annotate.
The theory is: use the same source for all arrows, but alter the target lat-lons (or more correctly, lon-lat). Seems obvious now.
Also, don't use annotate for city names. Annotate seems to put the name at the start of the arrow rather than the endpoint.
As I say, I'd welcome any suggestions for improvements (incl. for labelling).
import matplotlib.pyplot as plt
import cartopy.crs as ccrs
import cartopy.feature as cfeature
from pyproj import Proj, transform
def main():
ax = plt.axes(projection=ccrs.PlateCarree())
ax.set_extent([-150, 60, -25, 60])
ax.add_feature(cfeature.LAND)
ax.add_feature(cfeature.OCEAN)
ax.add_feature(cfeature.COASTLINE)
ax.add_feature(cfeature.BORDERS)
#Labels - city locations & names
ax.plot(77.20, 28.61, 'bo', markersize=7, transform=ccrs.Geodetic())
ax.text(65, 33, 'Dehli', transform=ccrs.Geodetic())
ax.plot(139.69, 35.68, 'bo', markersize=7, transform=ccrs.Geodetic())
ax.text(139.69, 35.68, 'Tokyo', transform=ccrs.Geodetic())
ax.plot(0.12, 51.50, 'bo', markersize=7, transform=ccrs.Geodetic())
ax.text(0.12, 51.50, 'London', transform=ccrs.Geodetic())
ax.plot(-71.05, 42.36, 'bo', markersize=7, transform=ccrs.Geodetic())
ax.text(-71.05, 42.36, 'New York', transform=ccrs.Geodetic())
ax.plot(151.81, -33.86, 'bo', markersize=7, transform=ccrs.Geodetic())
ax.text(151.81, -33.86, 'Sydney', transform=ccrs.Geodetic())
ax.plot(-43.2, -22.9, 'bo', markersize=7, transform=ccrs.Geodetic())
ax.text(-43.2, -22.9, 'Rio', transform=ccrs.Geodetic())
#Arrows lines
transform = ccrs.PlateCarree()._as_mpl_transform(ax)
#Dehli to Tokyo
ax.annotate('', xy=(139.69, 35.68), xytext=(77.20, 28.61),
xycoords='data',
size=20,
arrowprops=dict(facecolor='red', ec = 'none',
arrowstyle="fancy",
connectionstyle="arc3,rad=-0.3"))
#Dehli to London
ax.annotate('', xy=(0.12, 51.50), xytext=(77.20, 28.61),
size=10,
xycoords='data',
arrowprops=dict(facecolor='red', ec = 'none',
arrowstyle="fancy",
connectionstyle="arc3,rad=-0.3"))
#Dehli to New York
ax.annotate('', xy=(-71.05, 42.36), xytext=(77.20, 28.61),
xycoords='data',
size=30,
arrowprops=dict(facecolor='red', ec = 'none',
arrowstyle="fancy",
connectionstyle="arc3,rad=-0.3"))
#Dehli to Sydney
ax.annotate('', xy=(151.81, -33.86), xytext=(77.20, 28.61),
xycoords='data',
size=10,
arrowprops=dict(facecolor='red', ec = 'none',
arrowstyle="fancy",
connectionstyle="arc3,rad=-0.3"))
#Dehli to Rio
ax.annotate('', xy=(-43.2, -22.9), xytext=(77.20, 28.61),
xycoords='data',
size=20,
arrowprops=dict(facecolor='red', ec = 'none',
arrowstyle="fancy",
connectionstyle="arc3,rad=-0.3")
)
#plt.tight_layout()
plt.show()
if __name__ == '__main__':
main()