2016-03-16 2 views
2

Я пытаюсь написать программу python, которая отображает анимацию карты мира, в которой страны меняют цвет, исходя из того, насколько они используются в возобновляемых источниках энергии. Я пытаюсь, чтобы он отображал цвета для всех стран в 1960 году, затем цвета для всех стран в 1961 году, а затем в 1962 году ...Анимация с использованием Cartopy

Я использую картографию, чтобы добавить страны к фигуре и основываться их цвет от значений, которые я втягиваю в базу данных pandas из базы данных SQL. Я смог получить карту, чтобы показать, что я хочу в течение года: enter image description here

Однако я не могу понять, как его оживить. Я пытался использовать FuncAnimate, но я действительно пытаюсь понять, как это работает. На всех примерах, как представляется, есть функции, возвращающие строки, но я не являюсь графическими строками или контурами. Вот что я пробовал:

import sqlite3 
import pandas as pd 
import os 
import matplotlib.pyplot as plt 
import matplotlib as mpl 
import matplotlib.animation as animation 
import cartopy.crs as ccrs 
import cartopy.io.shapereader as shpreader 
from math import log 
from math import exp 
from matplotlib import colors 
path = 'H:/USER/DVanLunen/indicator_data/world-development-indicators/' 
os.chdir(path) 
con = sqlite3.connect('database.sqlite') 

# Grab : 
# % of electricity from renewable sources EG.ELC.RNWX.ZS 
# 1960 - 2013 

Indicator_df = pd.read_sql('SELECT * ' 
          'FROM Indicators ' 
          'WHERE IndicatorCode in(' 
                '"EG.ELC.RNWX.ZS"' 
                ')' 
          , con) 
# setup colorbar stuff and shape files 
norm = mpl.colors.Normalize(vmin=0, vmax=30) 
colors_in_map = [] 
for i in range(30): 
    val = log(i + 1, logbase)/log(31, logbase) 
    colors_in_map.append((1 - val, val, 0)) 
cmap = colors.ListedColormap(colors_in_map) 

shpfilename = shpreader.natural_earth(resolution='110m', 
             category='cultural', 
             name='admin_0_countries') 
reader = shpreader.Reader(shpfilename) 
countries_map = reader.records() 
logbase = exp(1) 
fig, ax = plt.subplots(figsize=(12, 6), 
         subplot_kw={'projection': ccrs.PlateCarree()}) 


def run(data): 
    """Update the Dist""" 
    year = 1960 + data % 54 
    logbase = exp(1) 
    for n, country in enumerate(countries_map): 
     facecolor = 'gray' 
     edgecolor = 'black' 
     indval = Indicator_df.loc[(Indicator_df['CountryName'] == 
            country.attributes['name_long']) & 
            (Indicator_df['Year'] == year), 'Value'] 
     if indval.any(): 
       greenamount = (log(float(indval) + 1, logbase)/
           log(31, logbase)) 
       facecolor = 1 - greenamount, greenamount, 0 
     ax.add_geometries(country.geometry, ccrs.PlateCarree(), 
          facecolor=facecolor, edgecolor=edgecolor) 
     ax.set_title('Percent of Electricity from Renewable Sources ' + 
        str(year)) 
    ax.figure.canvas.draw() 

cax = fig.add_axes([0.92, 0.2, 0.02, 0.6]) 
cb = mpl.colorbar.ColorbarBase(cax, cmap=cmap, norm=norm, 
           spacing='proportional') 
cb.set_label('%') 

ani = animation.FuncAnimation(fig, run, interval=200, blit=False) 
plt.show() 

Любая помощь была бы принята с благодарностью. Благодаря!

Некоторые примеры данных для Indicator_df (не реальный):

CountryName  Year  Value 
United States  1960  5 
United States  1961  10 
United States  1962  20 
United States  1963  30 
+0

Я в замешательстве ... что вы пытаетесь оживить? Вы хотите добавить страны по одному, или ... что? Кроме того, лучше указать пример с некоторыми точками данных вместо вашего SQL, чтобы другие могли его запускать. – Ajean

+0

Спасибо за ваш ответ! Я пытаюсь, чтобы он отображал цвета для всех стран в 1960 году, затем цвета для всех стран в 1961 году, затем в 1962 году ... Я добавил некоторые примеры данных для Соединенных Штатов 1960-1963 годов. – DVL

ответ

2

Есть на самом деле несколько проблем с тем, как вы уже настроили run(), но главная проблема возникла на самом деле быть enumate(countries_map). Функция records() возвращает генератор, который после того, как вы пропустили его один раз, кажется, не нравится, когда его запускают снова - я попробовал его отдельно от анимации, чтобы убедиться.

Тем не менее, проблему можно полностью устранить, переместив много кода из run(). В настоящее время, даже если это сработало, вы перерисовываете каждую страну в каждом кадре, а не только цвета. Это и интенсивное, и ненужное - вам не нужно рисовать серые несколько раз.

Я немного изменил ваш код и с поддельными данными, которые я вложил в США и Аргентину, он отлично работает для меня.

import pandas as pd 
import matplotlib.pyplot as plt 
import matplotlib as mpl 
import matplotlib.animation as animation 
import cartopy.crs as ccrs 
import cartopy.io.shapereader as shpreader 
from math import log 
from math import exp 
from matplotlib import colors 

# Grab : 
# % of electricity from renewable sources EG.ELC.RNWX.ZS 
# 1960 - 2013 

# Make fake data 
Indicator_df = pd.DataFrame({'CountryName':['United States']*4+['Argentina']*4, 
          'Year':[1960, 1961, 1962, 1963]*2, 
          'Value':[5, 10, 20, 30]*2}) 

# setup colorbar stuff and shape files 
norm = mpl.colors.Normalize(vmin=0, vmax=30) 
colors_in_map = [] 
logbase = exp(1) 
for i in range(30): 
    val = log(i + 1, logbase)/log(31, logbase) 
    colors_in_map.append((1 - val, val, 0)) 
cmap = colors.ListedColormap(colors_in_map) 

shpfilename = shpreader.natural_earth(resolution='110m', 
             category='cultural', 
             name='admin_0_countries') 
reader = shpreader.Reader(shpfilename) 
countries_map = reader.records() 

# These don't need to constantly be redefined, especially edgecolor 
facecolor = 'gray' 
edgecolor = 'black' 

fig, ax = plt.subplots(figsize=(12, 6), 
         subplot_kw={'projection': ccrs.PlateCarree()}) 

# Draw all the gray countries just once in an init function 
# I also make a dictionary for easy lookup of the geometries by country name later 
geom_dict = {} 
def init_run(): 
    for n, country in enumerate(countries_map): 
     ax.add_geometries(country.geometry, ccrs.PlateCarree(), 
          facecolor=facecolor, edgecolor=edgecolor) 
     geom_dict[country.attributes['name_long']] = country.geometry 


def run(data): 
    """Update the Dist""" 
    # "data" in this setup is a frame number starting from 0, so it corresponds nicely 
    # with your years 
    year = 1960 + data 

    # get a subset of the df for the current year 
    year_df = Indicator_df[Indicator_df['Year'] == year] 
    for i, row in year_df.iterrows(): 
     # This loops over countries, gets the value and geometry and adds 
     # the new-colored shape 
     geom = geom_dict[row['CountryName']] 
     value = row['Value'] 
     greenamount = (log(float(value) + 1, logbase)/
         log(31, logbase)) 
     facecolor = 1 - greenamount, greenamount, 0 
     ax.add_geometries(geom, ccrs.PlateCarree(), 
          facecolor=facecolor, edgecolor=edgecolor) 

    # I decreased the indent of this, you only need to do it once per call to run() 
    ax.set_title('Percent of Electricity from Renewable Sources ' + 
       str(year)) 

cax = fig.add_axes([0.92, 0.2, 0.02, 0.6]) 
cb = mpl.colorbar.ColorbarBase(cax, cmap=cmap, norm=norm, 
           spacing='proportional') 
cb.set_label('%') 

ani = animation.FuncAnimation(fig, run, init_func=init_run, frames=4, 
           interval=500, blit=False) 
plt.show() 

Основное отличие заключается в том, что я вообще не получаю доступ к грозобразующему устройству внутри функции запуска. При создании анимации единственной вещью, которая должна быть в функции запуска, является то, что изменить, вам не нужно повторно рисовать все в каждом кадре.

Тем не менее, это может быть даже лучше если вы просто держать художник с самим первым рисовать и просто изменения цвета его в режиме выбега, вместо того, чтобы делать новый ax.add_geometries. Вам нужно будет изучить, как изменить цвет элементарного FeatureArtist для этого.

+0

Большое спасибо! я очень ценю это – DVL

0

Просто для решения второго пункта о не рисовать всю фигуру снова:

Вместо того, чтобы хранить информацию формы, сохранить особенность художник, а именно:

feature_artist = ax.add_geometries(country.geometry, ccrs.PlateCarree(), 
         facecolor=facecolor, edgecolor=edgecolor) 
    geom_dict[country.attributes['name_long']] = feature_artist 

Затем, в процессе обновления цикл, вместо вызова ax.add_geometries снова вызовите следующее:

geom._feature._kwargs['facecolor'] = facecolor 

Это обновит facecolor. (Вы также можете изменить adgecolor - так как он остается неизменным, вы можете оставить это.)

Смежные вопросы