Skip To Content ArcGIS for Developers Sign In Dashboard

ArcGIS API for Python

Download the samples Try it live

Mapping the 2019 Novel Coronavirus Pandemic

According to WHO, 2019 Novel Corona Virus (COVID-19) is a virus (more specifically, a coronavirus) identified as the cause of an outbreak of respiratory illness, which was unknown before the outbreak began in Wuhan, China, in December 2019 [1]. Early on, the disease demonstrated an animal-to-person spread, then a person-to-person spread. Infections with COVID-19, were reported in a growing number of international locations, including the United States". The United States reported the first confirmed instance of person-to-person spread with this virus on January 30, 2020 [2].

This notebook shows how to use the ArcGIS API for Python to monitor the spread of COVID-19 as it became a pandemic.

1. Import Data

Esri provides an open-to-public and free-to-share feature layer that contains the most up-to-date COVID-19 cases covering China, the United States, Canada, Australia (at province/state level), and the rest of the world (at country level, represented by either the country centroids or their capitals). Data sources are WHO, US CDC, China NHC, ECDC, and DXY. The China data is updated automatically at least once per hour, and non-China data is updating manually. The data source repo that this layer referenced from, is created and maintained by the Center for Systems Science and Engineering (CSSE) at the Johns Hopkins University, and can be viewed here. In this notebook, we will use the feature layer supported by Esri Living Atlas team and JHU Data Services, and provide a different perspective in viewing the global maps of COVID-19 via the use of ArcGIS API for Python.

NOTE: "Since COVID-19 is continuously evolving, the sample reflects data as of July 4th, 2020 (or when the notebook is first published). Running this notebook at a later date might reflect a different result, but the overall steps should hold good."

DISCLAIMER: "This notebook is for the purpose of illustrating an analytical process using Jupyter Notebooks and ArcGIS API for Python, and should not be used as medical or epidemiological advice."

Necessary Imports

In [1]:
from io import BytesIO
import requests
import pandas as pd
from arcgis.features import FeatureLayer
from arcgis.gis import GIS
from arcgis.mapping import WebMap
In [2]:
"""
# if you are using arcgis api for python with version 1.8.0 or above,
# make sure that the pandas version>=1.0,
# if not, use `pip install --upgrade pandas>=1` to upgrade.
"""
pd.__version__
Out[2]:
'1.0.3'

Get data for the analysis

Query the source feature layer

The dashboard item contributed to the public by Esri and JHU CSSE, is accessible through here:

In [3]:
gis = GIS('home')
In [4]:
item = gis.content.search("Coronavirus_2019_nCoV_Cases owner:CSSE_GISandData", outside_org=True)[0]
item
Out[4]:
Coronavirus COVID-19 (2019-nCoV)
This ArcGIS Dashboard created by the CSSE Team at Johns Hopkins contains the most up-to-date coronavirus COVID-19 (2019-nCoV) cases and latest trend plot.Dashboard by CSSE_GISandData
Last Modified: July 03, 2020
0 comments, 1,719,759,357 views

Through the API Explorer provided along with the dashboard product, we can easily fetch the source URL for the Feature Service containing daily updated COVID-19 statistics, which can then be used to create a FeatureLayer object good for querying and visualizing.

In [5]:
src_url = "https://services1.arcgis.com/0MSEUqKaxRlEPj5g/arcgis/rest/services/Coronavirus_2019_nCoV_Cases/FeatureServer/1"
fl = FeatureLayer(url=src_url)
In [6]:
df_global = fl.query(where="1=1",
                     return_geometry=True,
                     as_df=True)

As stated in the dashboard, the source data can be grouped into:

  • A. Countries or regions of which data are collected at province/state level, e.g. China, the United States, Canada, Australia;
  • B. Countries or regions for the rest of the world of which data collected at country level, and shape represented by either the country centroids or their capitals;
  • C. Cruise Ships with confirmed COVID-19 cases.
Group A

Let us first take a look at how many countries are within group A, that Country_Region and Province_State are not null or NAN.

In [7]:
df_global[~pd.isnull(df_global['Province_State'])].groupby('Country_Region').sum()[['Confirmed', 'Recovered', 'Deaths']]
Out[7]:
Confirmed Recovered Deaths
Country_Region
Australia 8376 7366 104
Brazil 1539081 984615 63174
Canada 107143 70442 8732
Chile 291847 257451 6192
China 84850 79700 4641
Colombia 109793 45409 4001
Denmark 200 200 0
France 8676 5093 84
Germany 197198 181000 9020
India 648315 394227 18655
Italy 241419 191944 34854
Japan 19459 16798 977
Mexico 252165 195724 30366
Netherlands 213 187 19
Pakistan 225283 125094 4619
Peru 299080 189621 10412
Russia 673564 446127 10011
Spain 250545 150376 28385
Sweden 71419 0 5420
US 2838678 894325 129672
Ukraine 48628 21907 1243
United Kingdom 286412 1375 44283

Each country/region in Group A, has more than 1 feature, as what we have seen below from the query() results.

In [8]:
fset_usa = fl.query(where="Country_Region='US'")
fset_usa
Out[8]:
<FeatureSet> 60 features
In [9]:
fset_china = fl.query(where="Country_Region='China'")
fset_china
Out[9]:
<FeatureSet> 33 features
In [10]:
fl.query(where="Country_Region='Denmark'")
Out[10]:
<FeatureSet> 3 features
Group C

Group C contains cruise ships across the globe with reported cases:

In [11]:
df_cruise_ships = fl.query(where="Province_State='Diamond Princess' or \
                                  Province_State='Grand Princess' or \
                                  Country_Region='MS Zaandam' or \
                                  Country_Region='Diamond Princess'",
                           as_df=True)
In [12]:
df_cruise_ships[["Province_State", "Country_Region", "Last_Update", "Confirmed", "Recovered", "Deaths"]]
Out[12]:
Province_State Country_Region Last_Update Confirmed Recovered Deaths
0 Diamond Princess Canada 2020-07-05 00:34:01 0 0 1
1 Grand Princess Canada 2020-07-05 00:34:01 13 13 0
2 None Diamond Princess 2020-07-05 00:34:01 712 651 13
3 None MS Zaandam 2020-07-05 00:34:01 9 0 2
4 Grand Princess US 2020-07-05 00:34:01 103 0 3
5 Diamond Princess US 2020-07-05 00:34:01 49 0 0
Group B

In the df_global, other than the 22 countries (Australia, Canada, China, etc.) in Group A, and those cruise ships in Group C, all other countries/regions fall into Group B, e.g. Thailand. The major difference between Group A and Group B is that the latter contains one and only feature per country.

In [13]:
fl.query(where="Country_Region='Thailand'")
Out[13]:
<FeatureSet> 1 features

Query the reference feature layers

Because the geo-information provided by the dashboard contains only the coordinates representing the centroid of each country/region, the feature layer can only be rendered as points on Map. If this is what you want, you can now skip the rest of section 1, and jump right onto section 2.

On the other hand, if you want to visualize the confirmed/death/recovered cases per country/region as polygons, in other words as a choropleth map, please read along:

First, we need to access the feature service that contains geometry/shape info for all provinces in Mainland China, and merge with the COVID-19 DataFrame.

Access the reference feature layer of China
In [16]:
provinces_item = gis.content.get("0f57da7f853c4a1aa5b2e048ff8655d2")
provinces_item
Out[16]:
Merged_China_Province_Boundaries_2018
Feature layer generated from Merge LayersFeature Layer Collection by api_data_owner
Last Modified: July 04, 2020
0 comments, 5 views
In [27]:
provinces_flayer = provinces_item.layers[0]
provinces_df = provinces_flayer.query(as_df=True)
provinces_df.columns
Out[27]:
Index(['OBJECTID', 'ID', 'NAME', 'AREA', 'TOTPOP_CY', 'ISO_CODE', 'ISO_SUB',
       'ISO2_CC', 'ISO3_CC', 'Shape__Area', 'Shape__Length', 'ID_1',
       'sourceCountry', 'ENRICH_FID', 'aggregationMethod',
       'populationToPolygonSizeRating', 'apportionmentConfidence', 'HasData',
       'TOTPOP_CY_1', 'YEAR2018', 'SHAPE'],
      dtype='object')
In [28]:
tmp = provinces_df.sort_values('NAME', ascending=True)
provinces_df = tmp.drop_duplicates(subset='NAME', keep='last')
provinces_df.shape
Out[28]:
(31, 21)
DataFrame Merging for China Dataset

The subsets of dataframe being created in the previous section now needs to be merged with feature services which have geographic information (e.g. geometries, shape, or longitude/latitude) in order to provide location and geometries required for geographic mapping. First, let's acquire the geometries from feature services existing on living atlas or arcgis online organization to represent the geographic information needed of overlap_rows_china.

In [18]:
df_china = fset_china.sdf[['Province_State', 'Confirmed', 'Recovered', 'Deaths']]
df_china = df_china.assign(NAME = df_china["Province_State"])
df_china.head()
Out[18]:
Province_State Confirmed Recovered Deaths NAME
0 Anhui 991 985 6 Anhui
1 Beijing 926 594 9 Beijing
2 Chongqing 582 574 6 Chongqing
3 Fujian 363 360 1 Fujian
4 Gansu 164 154 2 Gansu

Because the names are inconsistent between the two data sources (e.g. provinces represented differently in df_china['Province_State'] and provinces_df['NAME"]), the replace_value_in_column method is declared below to edit the records and unify the column names.

In [19]:
def replace_value_in_column(data_frame, l_value, r_value, column_name = 'NAME'):
    data_frame.loc[data_frame[column_name] == l_value, column_name] = r_value

replace_value_in_column(df_china, 'Guangxi', 'Guangxi Zhuang Autonomous Region')
replace_value_in_column(df_china, 'Inner Mongolia', 'Inner Mongolia Autonomous Region')
replace_value_in_column(df_china, 'Ningxia', 'Ningxia Hui Autonomous Region')
replace_value_in_column(df_china, 'Tibet', 'Tibet Autonomous Region')

Now the two DataFrame objects have got unified column names, we can go ahead to use a single function in Pandas called merge as an entry point to perform in-memory standard database join operations (similar to that of relational databases such as SQL), and its syntax is shown here -

pd.merge(left, right, how='inner', on=None, left_on=None, right_on=None,
         left_index=False, right_index=False, sort=True)

Note that, the how argument specifies how to merge (a.k.a. how to determine which keys are to be included in the resulting table). If a key combination does not appear in either the left or the right tables, the values in the joined table will be NA. The table below then shows a summary of the how options and their SQL equivalent names −

Merge Method SQL Equivalent Description
left LEFT OUTER JOIN Use keys from left object
right RIGHT OUTER JOIN Use keys from right object
outer FULL OUTER JOIN Use union of keys
inner INNER JOIN Use intersection of keys

In this case, we will be calling merge() with how='inner' to perform the inner join of the two DataFrame objects on the index field "NAME" and only to keep the intersection of keys.

In [30]:
cols_2 = ['NAME', 'AREA', 'TOTPOP_CY','SHAPE']
overlap_rows_china = pd.merge(left = provinces_df[cols_2], right = df_china, 
                              how='inner', on = 'NAME')
overlap_rows_china.head()
Out[30]:
NAME AREA TOTPOP_CY SHAPE Province_State Confirmed Recovered Deaths
0 Anhui 140139.795987 61795934 {'rings': [[[116.36710167, 34.643320083], [116... Anhui 991 985 6
1 Beijing 16535.630276 22237467 {'rings': [[[116.647890001, 41.0513740000001],... Beijing 926 594 9
2 Chongqing 82390.600691 30365055 {'rings': [[[108.50186348, 32.20025444], [108.... Chongqing 582 574 6
3 Fujian 121789.666833 38659398 {'rings': [[[118.426801682, 28.2953205110001],... Fujian 363 360 1
4 Gansu 401138.394581 26268236 {'rings': [[[97.1721363070001, 42.793840408000... Gansu 164 154 2
In [41]:
cols_2 = ['NAME', 'AREA', 'TOTPOP_CY','SHAPE','Shape__Area', 'Shape__Length']
overlap_rows_china = pd.merge(left = provinces_df[cols_2], right = df_china, how='inner',
                        on = 'NAME')
overlap_rows_china.head()
Out[41]:
NAME AREA TOTPOP_CY SHAPE Shape__Area Shape__Length Province_State Confirmed Recovered Deaths
0 Beijing 1.653553e+04 21933363 {'rings': [[[116.817586898717, 39.614297867152... 1.748578 9.056859 Beijing 593 574 9
1 Tianjin 1.162387e+04 15535310 {'rings': [[[117.385320663218, 40.226179123367... 1.213896 8.534763 Tianjin 191 187 3
2 Hebei 1.873810e+05 74961654 {'rings': [[[114.12208366363, 40.7417316435006... 19.643388 51.344150 Hebei 328 321 6
3 Shanxi 1.567205e+05 37006516 {'rings': [[[114.12208366363, 40.7417316435006... 15.990927 26.372735 Shanxi 198 198 0
4 Inner Mongolia Autonomous Region 1.143798e+06 25392001 {'rings': [[[121.493043899656, 53.332681656033... 128.825703 131.282899 Inner Mongolia 209 194 1

As shown in overlap_rows_china, each province/state in China is now merged while retaining the SHAPE column, and is ready to be rendered as polygons.

Access the reference feature layer of the United States

Next, we need to access the feature service that contains geometry/shape info for all states in the U. S., and merge with the DataFrame depicting COVID-19 statistics.

In [31]:
us_states_item = gis.content.get('99fd67933e754a1181cc755146be21ca')
us_states_item
Out[31]:
USA States (Generalized)
USA States (Generalized) provides 2017 boundaries for the States of the United States in the 50 states and the District of Columbia.Feature Layer Collection by esri_dm
Last Modified: June 18, 2020
6 comments, 286,179,983 views
In [32]:
us_states_flayer = us_states_item.layers[0]
us_states_df = us_states_flayer.query(as_df=True)
us_states_df.columns
Out[32]:
Index(['FID', 'STATE_NAME', 'STATE_FIPS', 'SUB_REGION', 'STATE_ABBR',
       'POPULATION', 'POP_SQMI', 'POP2010', 'POP10_SQMI', 'WHITE', 'BLACK',
       'AMERI_ES', 'ASIAN', 'HAWN_PI', 'HISPANIC', 'OTHER', 'MULT_RACE',
       'MALES', 'FEMALES', 'AGE_UNDER5', 'AGE_5_9', 'AGE_10_14', 'AGE_15_19',
       'AGE_20_24', 'AGE_25_34', 'AGE_35_44', 'AGE_45_54', 'AGE_55_64',
       'AGE_65_74', 'AGE_75_84', 'AGE_85_UP', 'MED_AGE', 'MED_AGE_M',
       'MED_AGE_F', 'HOUSEHOLDS', 'AVE_HH_SZ', 'HSEHLD_1_M', 'HSEHLD_1_F',
       'MARHH_CHD', 'MARHH_NO_C', 'MHH_CHILD', 'FHH_CHILD', 'FAMILIES',
       'AVE_FAM_SZ', 'HSE_UNITS', 'VACANT', 'OWNER_OCC', 'RENTER_OCC',
       'NO_FARMS12', 'AVE_SIZE12', 'CROP_ACR12', 'AVE_SALE12', 'SQMI',
       'Shape__Area', 'Shape__Length', 'SHAPE'],
      dtype='object')
DataFrame Merging for U.S. Dataset
In [33]:
df_usa = fset_usa.sdf[['Province_State', 'Confirmed', 'Recovered', 'Deaths']]
df_usa = df_usa.assign(STATE_NAME = df_usa["Province_State"])
df_usa.head()
Out[33]:
Province_State Confirmed Recovered Deaths STATE_NAME
0 Mississippi 30674 0 1107 Mississippi
1 Grand Princess 103 0 3 Grand Princess
2 Oklahoma 15648 0 398 Oklahoma
3 Delaware 11996 0 512 Delaware
4 Minnesota 37624 0 1503 Minnesota
In [34]:
cols_4 = ['STATE_NAME','SHAPE']
overlap_rows_usa = pd.merge(left = us_states_df[cols_4], right = df_usa, 
                            how='inner', on = 'STATE_NAME')
overlap_rows_usa.head()
Out[34]:
STATE_NAME SHAPE Province_State Confirmed Recovered Deaths
0 Alaska {'rings': [[[-17959594.8053098, 8122953.575198... Alaska 1107 0 16
1 California {'rings': [[[-13543710.3257494, 4603367.827345... California 252527 0 6334
2 Hawaii {'rings': [[[-17819334.303422, 2512026.7784964... Hawaii 999 0 19
3 Idaho {'rings': [[[-13027307.5891034, 5415905.134774... Idaho 6994 0 93
4 Nevada {'rings': [[[-13263990.1054907, 4637763.931898... Nevada 21575 0 530
Access the reference feature layer of world countries
In [35]:
countries_item = gis.content.get('2b93b06dc0dc4e809d3c8db5cb96ba69')
countries_item
Out[35]:
World Countries (Generalized)
World Countries (Generalized) provides optimized country boundaries with useful name and country code attributes. Current to January 2020.Feature Layer Collection by esri_dm
Last Modified: March 03, 2020
1 comments, 28,482,720 views
In [36]:
countries_flayer = countries_item.layers[0]
countries_df = countries_flayer.query(as_df=True)
countries_df.columns
Out[36]:
Index(['FID', 'COUNTRY', 'ISO', 'COUNTRYAFF', 'AFF_ISO', 'Shape__Area',
       'Shape__Length', 'SHAPE'],
      dtype='object')
DataFrame Merging for global Dataset

The df_global has listed its Country_Region column with their current best-known names in English, while the countries_df uses their currently best-known equivalents, and this difference in naming countries has created a problem for the merge() operation to understand if the two countries listed in two DataFrame objects are the same. We need to hence run the following cell in order to make country names consistent between the two DataFrames to be merged.

In [37]:
df_global.loc[df_global['Country_Region']=='US', 'Country_Region'] = 'United States'
df_global.loc[df_global['Country_Region']=='Korea, South', 'Country_Region'] = 'South Korea'
df_global.loc[df_global['Country_Region']=='Korea, North', 'Country_Region'] = 'North Korea'
df_global.loc[df_global['Country_Region']=='Russia', 'Country_Region'] = 'Russian Federation'
df_global.loc[df_global['Country_Region']=='Czechia', 'Country_Region'] = 'Czech Republic'
List the top 10 countries with largest numbers

With df_global ready, we can now sort countries or regions by their numbers of confirmed/recovered/death cases, through usage of groupby(), and sort_values().

In [40]:
# sorted by # of confirmed cases
df_global_sum = df_global.groupby('Country_Region').sum()[['Confirmed', 'Recovered', 'Deaths']]
df_global_sum_c = df_global_sum.sort_values(by = ['Confirmed'], ascending = False)
df_global_sum_c.head(10)
Out[40]:
Confirmed Recovered Deaths
Country_Region
United States 2838678 894325 129672
Brazil 1539081 984615 63174
Russian Federation 673564 446127 10011
India 648315 394227 18655
Peru 299080 189621 10412
Chile 291847 257451 6192
United Kingdom 286412 1375 44283
Mexico 252165 195724 30366
Spain 250545 150376 28385
Italy 241419 191944 34854
In [41]:
# sorted by death tolls
df_global_sum_d = df_global_sum.sort_values(by = ['Deaths'], ascending = False)
df_global_sum_d.head(10)
Out[41]:
Confirmed Recovered Deaths
Country_Region
United States 2838678 894325 129672
Brazil 1539081 984615 63174
United Kingdom 286412 1375 44283
Italy 241419 191944 34854
Mexico 252165 195724 30366
France 204222 77185 29896
Spain 250545 150376 28385
India 648315 394227 18655
Iran 237878 198949 11408
Peru 299080 189621 10412
Joining the COVID-19 stats and world countries DataFrames
In [42]:
world_merged1 = pd.merge(df_global_sum_c, countries_df[['COUNTRY', 'SHAPE']], 
                         left_index=True, right_on='COUNTRY',
                         how="left")
world_merged1[['COUNTRY', 'Confirmed','Deaths', 'Recovered']].head(10)
Out[42]:
COUNTRY Confirmed Deaths Recovered
154.0 United States 2838678 129672 894325
20.0 Brazil 1539081 63174 984615
246.0 Russian Federation 673564 10011 446127
193.0 India 648315 18655 394227
18.0 Peru 299080 10412 189621
16.0 Chile 291847 6192 257451
89.0 United Kingdom 286412 44283 1375
12.0 Mexico 252165 30366 195724
247.0 Spain 250545 28385 150376
170.0 Italy 241419 34854 191944

Now, each country/region in world_merged1 is now merged with the SHAPE column, and is ready to be rendered as polygons.

2. Map the COVID-19 cases in China

Next, let us start visualizing the following scenarios targeting at China:

  • Confirmed cases rendered as points, and polygons
  • Death cases rendered as points, and polygons
  • Recovered cases rendered as points, and polygons

Map the confirmed COVID-19 cases in China

We can either call the add_layer() function to add the specified layer or item (i.e. the FeatureLayer object created as fl) to the map widget, and set the visualization options to be using ClassedSizeRenderer, or plot the derived SeDF on the map view directly with further descriptions such as how to renderer spatial data using symbol and color palette (Here, the SeDF is the derivative of the merged DataFrame, which now contains a SHAPE column that we can use to plot in the Map widget as polygons).

Display confirmed cases in China as points

In [30]:
map1 = gis.map('China', zoomlevel=4)
map1
Out[30]:
In [28]:
map1.add_layer(fl,   { "type": "FeatureLayer",
                       "renderer":"ClassedSizeRenderer",
                       "field_name":"Confirmed"})
In [29]:
map1.zoom = 3
map1.legend = True

Display confirmed cases in China as polygons

In [26]:
map1b = gis.map('China')
map1b
Out[26]:
In [44]:
map1b.clear_graphics()
overlap_rows_china.spatial.plot(  kind='map', map_widget=map1b,
                                  renderer_type='c',  # for class breaks renderer
                                  method='esriClassifyNaturalBreaks',  # classification algorithm
                                  class_count=4,  # choose the number of classes
                                  col='Confirmed',  # numeric column to classify
                                  cmap='inferno',  # color map to pick colors from for each class
                                  alpha=0.7  # specify opacity
                                 )
Out[44]:
True
In [45]:
map1b.zoom = 4
map1b.legend=True

The Map view above (map1b) displays the number of confirmed cases per province in Mainland China. Orange polygons refer to provinces with number of confirmed cases in the range of [45423, 68134], and black polygons represent those in the range of [1, 22712].

Also, we can save the MapView object into a Web Map item for the purpose of future references and modifications.

In [46]:
map1b.save({'title':'Confirmed COVID-19 Cases in China',
            'snippet':'Map created using Python API showing confirmed COVID-19 cases in China',
            'tags':['automation', 'COVID19', 'world health', 'python']})
Out[46]:
Confirmed COVID-19 Cases in China
Map created using Python API showing confirmed COVID-19 cases in ChinaWeb Map by arcgis_python
Last Modified: July 04, 2020
0 comments, 0 views

For example, we can browse the web map in the browser, change its symbology to different color maps in the configuration pane, then visualize it again with different looks here.

In [51]:
map1b_item = gis.content.search('Confirmed COVID-19 Cases in China')[0]
WebMap(map1b_item)
Out[51]:

Map the deaths caused by COVID-19 in China

Display death cases in China as points

In [34]:
map2 = gis.map('China', zoomlevel=4)
map2
Out[34]:
In [32]:
map2.add_layer(fl,   { "type": "FeatureLayer",
                       "renderer":"ClassedSizeRenderer",
                       "field_name":"Deaths"})
In [33]:
map2.legend = True

Display death cases in China as polygons

In [52]:
map2b = gis.map('China')
map2b
In [40]:
map2b = gis.map('China')
map2b
Out[40]:
In [53]:
map2b.clear_graphics()
overlap_rows_china.spatial.plot(  kind='map', map_widget=map2b,
                                  renderer_type='c',  # for class breaks renderer
                                  method='esriClassifyNaturalBreaks',  # classification algorithm
                                  class_count=4,  # choose the number of classes
                                  col='Deaths',  # numeric column to classify
                                  cmap='inferno',  # color map to pick colors from for each class
                                  alpha=0.7  # specify opacity
                                 )
Out[53]:
True
In [54]:
map2b.zoom = 4
map2b.legend = True

Using the same approach, we can then map the number of death cases per province in Mainland China. With legend displayed, map2b shows us orange polygons refer to provinces with number of death cases in the range of [3008, 4512], and black polygons represent those in the range of [0, 1504].

Similarly, we can create an additional deliverable - the Web Map Item created on the active GIS - and then browse the web map in the browser, change its symbology to different color maps in the configuration pane, and/or visualize it again with different looks here.

In [55]:
map2b_item = map2b.save({'title':'COVID-19 Death Cases in China',
                         'snippet':'Map created using Python API showing COVID-19 death cases in China',
                         'tags':['automation', 'COVID19', 'world health', 'python']})
In [58]:
WebMap(map2b_item)
Out[58]:

Map the recovered COVID-19 cases in China

Display the recovered cases in China as points

In [44]:
map3 = gis.map('China', zoomlevel=4)
map3
Out[44]:
In [42]:
map3.add_layer(fl,   { "type": "FeatureLayer",
                       "renderer":"ClassedSizeRenderer",
                       "field_name":"Recovered"})
In [43]:
map3.legend = True

Display the recovered cases in China as polygons

In [48]:
map3b = gis.map('China')
map3b
Out[48]: