I'd like to know how to fill in a map of U.S. counties by value (i.e., a chloropleth map), using Python 3 and Cartopy, and I haven't yet found anything online to guide me in that. That filled value could be, for instance, highest recorded tornado rating (with counties left blank for no recorded tornadoes), or even something arbitrary such as whether I've visited (=1) or lived (=2) in the county. I found a helpful MetPy example to get the county boundaries on a map:
https://unidata.github.io/MetPy/latest/examples/plots/US_Counties.html
What I envision is somehow setting a list (or dictionary?) of county names to a certain value, and then each value would be assigned to a particular fill color. This is my current script, which generates a nice blank county map of the CONUS/lower 48 (though I'd eventually also like to add Alaska/Hawaii insets).
import cartopy
import cartopy.crs as ccrs
import matplotlib as mpl
import matplotlib.pyplot as plt
from metpy.plots import USCOUNTIES
plot_type = 'png'
borders = cartopy.feature.BORDERS
states = cartopy.feature.NaturalEarthFeature(category='cultural', scale='10m', facecolor='none', name='admin_1_states_provinces_lakes')
oceans = cartopy.feature.OCEAN
lakes = cartopy.feature.LAKES
mpl.rcParams['figure.figsize'] = (12,10)
water_color = 'lightblue'
fig = plt.figure()
ax = plt.axes(projection=ccrs.LambertConformal(central_longitude=-97.5, central_latitude=38.5, standard_parallels=(38.5,38.5)))
ax.set_extent([-120, -74, 23, 50], ccrs.Geodetic())
ax.coastlines()
ax.add_feature(borders, linestyle='-')
ax.add_feature(states, linewidth=0.50, edgecolor='black')
ax.add_feature(oceans, facecolor=water_color)
ax.add_feature(lakes, facecolor=water_color, linewidth=0.50, edgecolor='black')
ax.add_feature(USCOUNTIES.with_scale('500k'), linewidth=0.10, edgecolor='black')
plt.savefig('./county_map.'+plot_type)
plt.close()
Any ideas or tips on how to assign values to counties and fill them accordingly?
So Cartopy's shapereader.Reader can give you access to all of the records in the shapefile, including their attributes. Putting this together with MetPy's get_test_data to get access to the underlying shapefile you can get what you want, assuming you have a dataset that maps e.g. FIPSCODE to EF rating:
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1, projection=ccrs.PlateCarree())
cmap = plt.get_cmap('magma')
norm = plt.Normalize(0, 5)
# Fake tornado dataset with a value for each county code
tor_data = dict()
# This will only work (have access to the shapefile's database of
# attributes after it's been download by using `USCOUNTIES` or
# running get_test_data() for the .shx and .dbf files as well.
for rec in shpreader.Reader(get_test_data('us_counties_20m.shp',
as_file_obj=False)).records():
# Mimic getting data, but actually getting a random number
# GEOID seems to be the FIPS code
max_ef = tor_data.get(rec.attributes['GEOID'], np.random.randint(0, 5))
# Normalize the data to [0, 1] and colormap manually
color = tuple(cmap(norm(max_ef)))
# Add the geometry to the plot, being sure to specify the coordinate system
ax.add_geometries([rec.geometry], crs=ccrs.PlateCarree(), facecolor=color)
ax.set_extent((-125, -65, 25, 48))
That gives me:
I'm not sure about passing in a dict, but you can pass in a list to facecolor.
ax.add_feature(USCOUNTIES.with_scale('500k'), linewidth=0.10, edgecolor='black', facecolor=["red", "blue", "green"])
If you know how many counties there are you can make a list that long by:
import matplotlib.cm as cm
import numpy as np
number_of_counties = 3000
color_scale = list(cm.rainbow(np.linspace(0, 1, number_of_counties)))
ax.add_feature(USCOUNTIES.with_scale('500k'), linewidth=.10, edgecolor="black", facecolor=color_scale)
but they didn't make it easy to extract the names from USCOUNTIES. You can see where it is defined in your source code:
from metpy import plots
print(plots.__file__)
If you go inside the directory printed there is a file named cartopy_utils.py and inside the class definition for class MetPyMapFeature(Feature): you will see USCOUNTIES. You might have better luck than I did mapping county names to the geometric shapes.
EDIT: Also, I just used cm.rainbow as an example, you can choose from any color map https://matplotlib.org/stable/tutorials/colors/colormaps.html. Not sure if it even goes up to 3000, but you get the idea.
i have a dataset longitude, latitude, its city, and the status of its city of coronavirus.
I want to give a label on each point for city name. i dont have any idea if i use plt.text() one by one to give the labels.
Here the code i use for creating dataset
jabar = [
['Depok',-6.385589,106.830711,'sedang',600],
['Tasikmalaya',-7.319563,108.202972,'sedang',600],
['Ciamis',-7.3299,108.3323,'sedang',600],
['Kuningan',-7.0138,108.5701,'sedang',600],
['Bogor',-6.497641,106.828224,'sedang',600],
['Bogor',-6.595038,106.816635,'sedang',600],
['Cirebon',-6.737246,108.550659,'sedang',600],
['Majalengka',-6.8364,108.2274,'sedang',600],
['Sumedang',-6.8381,107.9275,'sedang',600],
['Indramayu',-6.327583,108.324936,'sedang',600],
['Subang',-6.571589,107.758736,'sedang',600],
['Purwakarta',-6.538681,107.449944,'sedang',600],
['Karawang',-6.3227,107.3376,'sedang',600],
['Bekasi',-6.241586,106.992416,'sedang',600],
['Pangandaran',-7.6833,108.6500,'sedang',600],
['Sukabumi',-6.923700,106.928726,'sedang',600],
['Cimahi',-6.8841,107.5413,'sedang',600],
['Banjar',-7.374585,108.558189,'sedang',600],
['Cianjur',-6.734679,107.041252,'sedang',600],
['Bandung',-6.914864,107.608238,'tinggi',1000],
['Bandung',-6.905977,107.613144,'tinggi',1000],
['Bandung',-6.914744,107.609810,'tinggi',1000],
['Garut',-7.227906,107.908699,'sedang',600],
['Bandung Barat',-7.025253,107.519760,'sedang',600]]
features=['City','longitude','latitude','status','status_size']
risk_map = pd.DataFrame(jabar, columns=features)
and here it is the code i create for visualize to give the label each points.
import matplotlib.pyplot as plt
plt.figure(figsize=(14,8))
plt.scatter(risk_map['latitude'],risk_map['longitude'], c='orange',
s=risk_map['status_size'], label='Risk region')
plt.title('Peta Sebaran Covid-19', fontsize=20)
plt.text(-7.227906,107.908699,'Garut')
plt.show()
actually i have two datasets exclude the code i write above, the another is about confirmed-positive-cases-covid-region which is the point about more than 500.000 points.
I merge this two dataset to get the risk-region. But i get trouble when i want to giva a labels on each point.
the plt.text() i write above is example to give a label on a point. it is impossible if i write one by one as same as the text code because my computer got cracked and blank after i executed that code.
Anyone have any idea to give a label on each points that i write the code above?
thank in advance
plotly mapbox provides very simple to use capabilities for what you want
your longitude, latitude values are reversed. See in code sample below I've reversed them
import plotly.express as px
import pandas as pd
jabar = [
['Depok',-6.385589,106.830711,'sedang',600],
['Tasikmalaya',-7.319563,108.202972,'sedang',600],
['Ciamis',-7.3299,108.3323,'sedang',600],
['Kuningan',-7.0138,108.5701,'sedang',600],
['Bogor',-6.497641,106.828224,'sedang',600],
['Bogor',-6.595038,106.816635,'sedang',600],
['Cirebon',-6.737246,108.550659,'sedang',600],
['Majalengka',-6.8364,108.2274,'sedang',600],
['Sumedang',-6.8381,107.9275,'sedang',600],
['Indramayu',-6.327583,108.324936,'sedang',600],
['Subang',-6.571589,107.758736,'sedang',600],
['Purwakarta',-6.538681,107.449944,'sedang',600],
['Karawang',-6.3227,107.3376,'sedang',600],
['Bekasi',-6.241586,106.992416,'sedang',600],
['Pangandaran',-7.6833,108.6500,'sedang',600],
['Sukabumi',-6.923700,106.928726,'sedang',600],
['Cimahi',-6.8841,107.5413,'sedang',600],
['Banjar',-7.374585,108.558189,'sedang',600],
['Cianjur',-6.734679,107.041252,'sedang',600],
['Bandung',-6.914864,107.608238,'tinggi',1000],
['Bandung',-6.905977,107.613144,'tinggi',1000],
['Bandung',-6.914744,107.609810,'tinggi',1000],
['Garut',-7.227906,107.908699,'sedang',600],
['Bandung Barat',-7.025253,107.519760,'sedang',600]]
features=['City','longitude','latitude','status','status_size']
risk_map = pd.DataFrame(jabar, columns=features)
fig = px.scatter_mapbox(risk_map, lon="latitude", lat="longitude",
color="status", hover_name="City",size="status_size"
)
fig.update_layout(mapbox={"style":"carto-positron"})
fig
I am trying to generate several maps with different content based on a dataframe.
So far, I have managed to display the information I needed on the interactive maps.
However, as I need to include the generated maps as figures in a report, I need to find a way to show all the markers in the figures. Problem is: some markers only are shown when I manually zoom in the area.
Is there a way to always make the markers visible?
Here is the code:
import plotly.graph_objects as go
token = open("token.mapbox_token").read() # you need your own token
df_select = df_map.loc[df_map['Budget'] == 0.9]
fig= go.Figure(go.Scattermapbox(lat=df_select.Latitude, lon=df_select.Longitude,
mode='markers', marker=go.scattermapbox.Marker(
size=df_select.Warehouse_Size*5, color = df_select.Warehouse_Size,
colorscale = ['white','red','orange','green','blue','purple'],
showscale = False)))
fig = fig.add_trace(go.Choroplethmapbox(geojson=br_geo, locations=df_select.State,
featureidkey="properties.UF_05",
z=df_select.Top10,
colorscale=["white","pink"], showscale=False,
zmin = 0,
zmax=1,
marker_opacity=0.5, marker_line_width=1
))
df_prio = df_select.loc[df_select['Prioritisated'] == 1]
fig= fig.add_trace(go.Scattermapbox(lat=df_prio.Latitude, lon=df_prio.Longitude+1,
mode='markers',
marker=go.scattermapbox.Marker(symbol = "campsite", size = 10)))
fig.update_layout(height=850,width = 870,
mapbox_style = "mapbox://styles/rafaelaveloli/ckollp2dg21dd19pmgm3vyebu",
mapbox_zoom=3.4, mapbox_center = {"lat": -14.5 ,"lon": -52},
mapbox_accesstoken = token, showlegend= False)
fig.show()
This is the result I get:
And this is one of the hidden markers that are only visible when zooming in:
How can I make it visible in the first figure, without changing the figure zoom and dimensions?
Passing allowoverlap=True to go.scattermapbox.Marker() seems to resolve the issue (link to relevant docs).