text: import requests
  import pandas as pd
  import geopandas as gpd
  
  from shapely.geometry import Polygon, MultiPolygon
  from multiprocessing.dummy import Pool as ThreadPool
  from tqdm import tqdm
  
  from gcj2wgs import converter
  
  def get_adcode():
      '''
      Get the name and adcode of each administrative divisions from gaode's api/district
      https://lbs.amap.com/api/webservice/guide/api/district
      '''
      
      url = 'http://restapi.amap.com/v3/config/district'
      params = {
          'keywords':'中华人民共和国',
          'key':api_key,
          'subdistrict':3,
          'extensions':'base'
      }
      geo = requests.get(url, params).json()
  
      ## flat the json, get adcode
      df = pd.json_normalize(geo['districts'], 
                             record_path=['districts','districts','districts'],
                             meta=[['adcode'],['adcode','adcode'],['adcode','adcode','adcode'],['name'],['name','name'],['name','name','name']],
                                  meta_prefix='_',
                                  sep='_').rename({'adcode':'ad_3','_adcode_adcode_adcode':'ad_2','_adcode_adcode':'ad_1','_adcode':'ad_0',
                                                   'name':'name_3','_name_name_name':'name_2','_name_name':'name_1','_name':'name_0'}, axis=1)
  
      ## administrative divisions without level 3
      index = df.ad_3 == df.ad_2
      df.loc[index, 'name_3'] = df.loc[index, 'name_2']
  
      ## administrative divisions without level 2
      index = df.ad_1.isin(['110000','120000','310000','500000'])
      df.loc[index, 'ad_2'] = df.loc[index, 'ad_1']
      df.loc[index, 'name_2'] = df.loc[index, 'name_1']
  
      ## administrative divisions without level 2-3
      new = pd.DataFrame([['100000']+['710000']*3+['中华人民共和国']+['台湾省']*3,
                          ['100000']+['810000']*3+['中华人民共和国']+['香港特别行政区']*3,
                          ['100000']+['820000']*3+['中华人民共和国']+['澳门特别行政区']*3
                         ], columns = ['ad_'+str(x) for x in range(0,4)] + ['name_'+str(x) for x in range(0,4)])
      df = pd.concat([df, new]).reset_index(drop=True)
  
      adcode2name = {}
      for i in range(0,4):
          temp = {x:y for x,y in zip(df[['ad_'+str(x) for x in range(i+1)]].apply(lambda x: ';'.join(x), axis=1), 
                                     df[['name_'+str(x) for x in range(i+1)]].apply(lambda x: ';'.join(x), axis=1))}
          adcode2name.update(temp)
      return(adcode2name)
  
  def get_geofence(full_adcode):
      '''
      Fetch the geofence of each adcode
      '''
      adcode = full_adcode.split(';')[-1]
      url = 'http://restapi.amap.com/v3/config/district'
      params = {
          'keywords':adcode,
          'key':api_key,
          'subdistrict':0,
          'extensions':'all'
      }
      geo = requests.get(url, params).json()
      name = geo['districts'][0]['name']
      polyline = geo['districts'][0]['polyline']
  
      geom_gcj = MultiPolygon([Polygon([tuple(float(z) for z in y.split(',')) for y in x.split(';')]) for x in polyline.split('|')])
      geom_wgs = MultiPolygon([Polygon([converter(*tuple(float(z) for z in y.split(','))) for y in x.split(';')]) for x in polyline.split('|')])
  
      return([name, adcode2name.get(full_adcode), adcode, full_adcode, geom_gcj.buffer(0), geom_wgs.buffer(0)])
  
  if __name__ == '__main__':
  
      ## https://lbs.amap.com/api/webservice/guide/create-project/get-key
      ## may need developer verification
  
      api_key = ''
      if not api_key:
          raise ValueError('need an valid gaode api_key!')
  
      adcode2name = get_adcode()
  
      ## lower then number of threads if no developer verification
      nthreads = 16
      with ThreadPool(nthreads) as pool:
          temp = list(tqdm(pool.imap(get_geofence, list(adcode2name.keys())), total=len(adcode2name)))
          
      geofence = pd.DataFrame(temp, columns = ['name', 'full_name', 'ad', 'full_ad','geom_gcj', 'geom_wgs'])
      geofence.loc[:, 'level'] = geofence.full_name.str.count(';')
  
      ## save
      for i,j in geofence.groupby('level'):
          ## GeoPackage
          gpd.GeoDataFrame(j.drop('geom_wgs',axis=1), geometry='geom_gcj').to_file('gpkg/gcj02/level_'+str(i)+'.gpkg', driver="GPKG")
          gpd.GeoDataFrame(j.drop('geom_gcj',axis=1), geometry='geom_wgs').to_file('gpkg/wgs84/level_'+str(i)+'.gpkg', driver='GPKG')
          ## Shapefile
          gpd.GeoDataFrame(j.drop('geom_wgs',axis=1), geometry='geom_gcj').to_file('shp/gcj02/level_'+str(i)+'.shp', encoding='utf-8')
          gpd.GeoDataFrame(j.drop('geom_gcj',axis=1), geometry='geom_wgs').to_file('shp/wgs84/level_'+str(i)+'.shp', encoding='utf-8')
  