This notebook extracts precipitation timeseries for in districts in Uganda from NASA GPM satalite data


In [1]:
import pandas as pd
import geopandas as gp
from rasterstats import zonal_stats
from affine import Affine
import matplotlib as mpl
import matplotlib.pyplot as plt
import mpl_toolkits
from mpl_toolkits.basemap import Basemap
import numpy as np
import h5py
from osgeo import gdal
from descartes import PolygonPatch
import datetime
from __future__ import print_function
pd.set_option('io.hdf.default_format', 'table')
%matplotlib inline

In [2]:
from shapely.geometry.polygon import Polygon
def small_triangle(x, y):
    """
    Make a small triangle at the given point.
    """
    return Polygon([[x, y], [x, y + .001], [x + .001, y]])

# Load district shape files.
# TODO Clip Lake Victoria?
uganda_shapes = gp.GeoDataFrame.from_file("/home/nathan/GPM-district-precipitation/District_Boundaries_2014.shp")
# Add a weather stations for comparison with on the ground data.
uganda_shapes = uganda_shapes.append(dict(
    DNAME2014="HUEN Weather Station",
    geometry=small_triangle(32.444, 0.042)
), ignore_index=True)
uganda_shapes = uganda_shapes.append(dict(
    DNAME2014="Kitale Weather Station",
    geometry=small_triangle(35, 1.016)
), ignore_index=True)
uganda_shapes.plot(column='POP_2014', scheme='QUANTILES', k=3, colormap='OrRd')
def capitalize(s):
    return " ".join(
        map(lambda k: k[0].upper() + k[1:], s.lower().split(' ')))
uganda_shapes['district'] = uganda_shapes.DNAME2014.map(capitalize)
uganda_shapes.sort('district')


/home/nathan/venvs/main/local/lib/python2.7/site-packages/geopandas/plotting.py:225: FutureWarning: 'colormap' is deprecated, please use 'cmap' instead (for consistency with matplotlib)
  "(for consistency with matplotlib)", FutureWarning)
/home/nathan/venvs/main/local/lib/python2.7/site-packages/pysal/esda/mapclassify.py:259: RuntimeWarning: invalid value encountered in greater
  binIds += (x > l) * (x <= r) * k
/home/nathan/venvs/main/local/lib/python2.7/site-packages/pysal/esda/mapclassify.py:259: RuntimeWarning: invalid value encountered in less_equal
  binIds += (x > l) * (x <= r) * k
/home/nathan/venvs/main/local/lib/python2.7/site-packages/numpy/lib/function_base.py:3569: RuntimeWarning: Invalid value encountered in median
  RuntimeWarning)
/home/nathan/venvs/main/lib/python2.7/site-packages/ipykernel/__main__.py:25: FutureWarning: sort(columns=....) is deprecated, use sort_values(by=.....)
Out[2]:
ACCS_ELE_H CreationDa Creator DNAME2014 EditDate Editor F1969_2014 F1991_2002 F2002_2014 FEMALE2014 ... POP2013 POP_2014 POP_RURAL POP_URBAN REGION STATUS SUBREGION TotCon2011 geometry district
0 1.504846 2015-06-25T21:27:27.451Z Energy_Sector_WG ABIM 2015-06-25T21:27:27.451Z Energy_Sector_WG 4.20 0.73 6.20 56076.0 ... 57200.0 109039.0 91639.0 17400.0 NORTHERN REGION Electrified KARAMOJA 211.000000 POLYGON ((33.6125822004397 3.13106372913647, 3... Abim
1 0.589424 2015-06-25T21:27:27.451Z Energy_Sector_WG ADJUMANI 2015-06-25T21:27:27.451Z Energy_Sector_WG 3.80 6.37 1.17 121310.0 ... 399700.0 232813.0 189791.0 43022.0 NORTHERN REGION Electrified WEST NILE 315.000000 POLYGON ((32.0459654664137 3.58895935504548, 3... Adjumani
2 0.991467 2015-06-25T21:27:27.451Z Energy_Sector_WG AGAGO 2015-06-25T21:27:27.451Z Energy_Sector_WG 2.57 5.17 1.77 117391.0 ... 314700.0 227486.0 198319.0 29167.0 NORTHERN REGION Electrified ACHOLI 482.000000 POLYGON ((33.4165230894568 3.30434434518088, 3... Agago
3 0.000000 2015-06-25T21:27:27.451Z Energy_Sector_WG ALEBTONG 2015-06-25T21:27:27.451Z Energy_Sector_WG 2.83 3.17 2.70 116051.0 ... 233400.0 225327.0 218699.0 6628.0 NORTHERN REGION Electrified LANGO 0.000000 POLYGON ((33.0362912666343 2.50129284067751, 3... Alebtong
4 0.000000 2015-06-25T21:27:27.451Z Energy_Sector_WG AMOLATAR 2015-06-25T21:27:27.451Z Energy_Sector_WG 2.78 2.91 3.53 74412.0 ... 130900.0 146904.0 125470.0 21434.0 NORTHERN REGION Electrified LANGO 0.000000 POLYGON ((32.9013967347514 1.80569978194049, 3... Amolatar
5 0.000000 2015-06-25T21:27:27.451Z Energy_Sector_WG AMUDAT 2015-06-25T21:27:27.451Z Energy_Sector_WG 2.62 14.78 4.70 53260.0 ... 120500.0 111758.0 100141.0 11617.0 NORTHERN REGION Electrified KARAMOJA 0.000000 POLYGON ((34.9276340827004 2.28366970260547, 3... Amudat
6 0.000000 2015-06-25T21:27:27.451Z Energy_Sector_WG AMURIA 2015-06-25T21:27:27.451Z Energy_Sector_WG 2.21 8.18 3.40 139068.0 ... 441200.0 270601.0 263535.0 7066.0 EASTERN REGION Electrified TESO 0.000000 POLYGON ((33.7335661566546 2.41621934573676, 3... Amuria
7 0.047029 2015-06-25T21:27:27.451Z Energy_Sector_WG AMURU 2015-06-25T21:27:27.451Z Energy_Sector_WG 2.55 3.65 2.81 98014.0 ... 183600.0 190174.0 180328.0 9846.0 NORTHERN REGION Electrified ACHOLI 16.862725 POLYGON ((32.063290852887 3.57569809287813, 32... Amuru
8 0.567966 2015-06-25T21:27:27.451Z Energy_Sector_WG APAC 2015-06-25T21:27:27.451Z Energy_Sector_WG 3.35 3.70 3.25 188677.0 ... 360500.0 368786.0 346644.0 22142.0 NORTHERN REGION Electrified LANGO 374.604496 POLYGON ((32.5744186400744 2.21842203528725, 3... Apac
9 1.140154 2015-06-25T21:27:27.451Z Energy_Sector_WG ARUA 2015-06-25T21:27:27.451Z Energy_Sector_WG 2.90 3.58 2.92 431083.0 ... 801400.0 793537.0 730880.0 62657.0 NORTHERN REGION Electrified WEST NILE 1684.000000 POLYGON ((31.1763949194215 3.34610823634221, 3... Arua
10 1.689702 2015-06-25T21:27:27.451Z Energy_Sector_WG BUDAKA 2015-06-25T21:27:27.451Z Energy_Sector_WG 3.20 2.64 3.53 108243.0 ... 183700.0 208439.0 184605.0 23834.0 EASTERN REGION Electrified TESO 609.279422 POLYGON ((33.9745135128531 1.18976997680469, 3... Budaka
11 0.568825 2015-06-25T21:27:27.451Z Energy_Sector_WG BUDUDA 2015-06-25T21:27:27.451Z Energy_Sector_WG 3.10 3.78 4.52 105745.0 ... 187600.0 211683.0 204953.0 6730.0 EASTERN REGION Electrified ELGON 184.993267 POLYGON ((34.5345589222934 1.10512296622223, 3... Bududa
12 1.052812 2015-06-25T21:27:27.451Z Energy_Sector_WG BUGIRI 2015-06-25T21:27:27.451Z Energy_Sector_WG 3.45 3.80 3.16 198751.0 ... 447200.0 390076.0 361063.0 29013.0 EASTERN REGION Electrified EAST CENTRAL 742.470908 (POLYGON ((33.6154201985095 0.454442379513343,... Bugiri
13 0.000000 2015-06-25T21:27:27.451Z Energy_Sector_WG BUHWEJU 2015-06-25T21:27:27.451Z Energy_Sector_WG 3.52 3.43 3.36 63735.0 ... 103200.0 124044.0 121182.0 2862.0 WESTERN REGION Electrified SOUTH WESTERN 0.000000 POLYGON ((30.3167976825126 -0.190021496427814,... Buhweju
14 8.838427 2015-06-25T21:27:27.451Z Energy_Sector_WG BUIKWE 2015-06-25T21:27:27.451Z Energy_Sector_WG 2.58 2.36 2.33 222963.0 ... 441100.0 436406.0 290234.0 146172.0 CENTRAL REGION Electrified CENTRAL 2 7702.126054 POLYGON ((33.045614225198 0.503826041504873, 3... Buikwe
15 0.742404 2015-06-25T21:27:27.451Z Energy_Sector_WG BUKEDEA 2015-06-25T21:27:27.451Z Energy_Sector_WG 2.42 4.17 3.61 98122.0 ... 194400.0 188918.0 182649.0 6269.0 EASTERN REGION Electrified TESO 240.130403 POLYGON ((34.2295072646006 1.57933497190371, 3... Bukedea
16 1.961577 2015-06-25T21:27:27.451Z Energy_Sector_WG BUKOMANSIMBI 2015-06-25T21:27:27.451Z Energy_Sector_WG 1.38 0.84 0.66 76670.0 ... 155400.0 151075.0 141393.0 9682.0 CENTRAL REGION Electrified CENTRAL 1 723.206827 POLYGON ((31.6375722436633 0.0380275947998006,... Bukomansimbi
17 0.000000 2015-06-25T21:27:27.451Z Energy_Sector_WG BUKWO 2015-06-25T21:27:27.451Z Energy_Sector_WG 3.64 4.00 5.09 44483.0 ... 76300.0 90139.0 82453.0 7686.0 EASTERN REGION Under Construction ELGON 0.000000 POLYGON ((34.7581129075179 1.40945870415923, 3... Bukwo
18 0.606948 2015-06-25T21:27:27.451Z Energy_Sector_WG BULAMBULI 2015-06-25T21:27:27.451Z Energy_Sector_WG 3.00 3.51 5.00 91485.0 ... 128600.0 177322.0 161155.0 16167.0 EASTERN REGION Electrified ELGON 155.974043 POLYGON ((34.3489093181429 1.56723075862713, 3... Bulambuli
19 0.000000 2015-06-25T21:27:27.451Z Energy_Sector_WG BULIISA 2015-06-25T21:27:27.451Z Energy_Sector_WG 3.15 2.43 4.86 55493.0 ... 82800.0 113569.0 106284.0 7285.0 WESTERN REGION Electrified WESTERN 0.000000 POLYGON ((31.5727530422046 2.28512557255063, 3... Buliisa
20 1.555450 2015-06-25T21:27:27.451Z Energy_Sector_WG BUNDIBUGYO 2015-06-25T21:27:27.451Z Energy_Sector_WG 2.64 4.66 2.87 116125.0 ... 275100.0 224145.0 188947.0 35198.0 WESTERN REGION Electrified WESTERN 653.000000 POLYGON ((30.1217718901263 0.891014410679566, ... Bundibugyo
21 4.587373 2015-06-25T21:27:27.451Z Energy_Sector_WG BUSHENYI 2015-06-25T21:27:27.451Z Energy_Sector_WG 2.05 2.10 1.13 151786.0 ... 256500.0 235621.0 194558.0 41063.0 WESTERN REGION Electrified SOUTH WESTERN 2492.560615 POLYGON ((30.0919352849071 -0.352418624498652,... Bushenyi
22 2.427696 2015-06-25T21:27:27.451Z Energy_Sector_WG BUSIA 2015-06-25T21:27:27.451Z Energy_Sector_WG 2.85 2.73 3.08 169219.0 ... 306000.0 325527.0 269569.0 55958.0 EASTERN REGION Electrified EAST CENTRAL 1443.114942 POLYGON ((34.0687279889099 0.5923805845944971,... Busia
23 0.859116 2015-06-25T21:27:27.451Z Energy_Sector_WG BUTALEJA 2015-06-25T21:27:27.451Z Energy_Sector_WG 3.11 3.34 3.71 126805.0 ... 228800.0 245873.0 209624.0 36249.0 EASTERN REGION Electrified ELGON 357.446249 POLYGON ((34.0802517785972 1.04045513476829, 3... Butaleja
24 4.122824 2015-06-25T21:27:27.451Z Energy_Sector_WG BUTAMBALA 2015-06-25T21:27:27.451Z Energy_Sector_WG 1.69 1.36 1.22 51337.0 ... 100900.0 100471.0 85275.0 15196.0 CENTRAL REGION Electrified CENTRAL 1 944.926164 POLYGON ((32.2694631641008 0.314874879612362, ... Butambala
25 0.000000 2015-06-25T21:27:27.451Z Energy_Sector_WG BUVUMA 2015-06-25T21:27:27.451Z Energy_Sector_WG 7.16 7.13 6.25 41876.0 ... 56800.0 89960.0 80152.0 9808.0 CENTRAL REGION Not Electrified CENTRAL 2 0.000000 POLYGON ((32.8751338168278 -0.9994193916427599... Buvuma
26 0.000000 2015-06-25T21:27:27.451Z Energy_Sector_WG BUYENDE 2015-06-25T21:27:27.451Z Energy_Sector_WG 3.73 3.26 4.30 124432.0 ... 273900.0 320468.0 297429.0 23039.0 EASTERN REGION Electrified EAST CENTRAL 0.000000 POLYGON ((32.8956550151707 1.4811919503359, 32... Buyende
27 0.124959 2015-06-25T21:27:27.451Z Energy_Sector_WG DOKOLO 2015-06-25T21:27:27.451Z Energy_Sector_WG 2.88 3.60 2.87 93929.0 ... 189700.0 182579.0 162769.0 19810.0 NORTHERN REGION Electrified LANGO 42.713020 POLYGON ((33.1488299605389 2.10319662056038, 3... Dokolo
28 1.570042 2015-06-25T21:27:27.451Z Energy_Sector_WG GOMBA 2015-06-25T21:27:27.451Z Energy_Sector_WG 1.74 0.93 1.53 78555.0 ... 154900.0 160075.0 147632.0 12443.0 CENTRAL REGION Electrified CENTRAL 1 552.754975 POLYGON ((31.5942784802293 0.343469278595732, ... Gomba
29 4.869975 2015-06-25T21:27:27.451Z Energy_Sector_WG GULU 2015-06-25T21:27:27.451Z Energy_Sector_WG 3.14 2.94 3.30 228123.0 ... 407500.0 443733.0 291457.0 152276.0 NORTHERN REGION Electrified ACHOLI 3840.775760 POLYGON ((32.3601217660049 3.30087097163236, 3... Gulu
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
82 5.554725 2015-06-25T21:27:27.451Z Energy_Sector_WG MPIGI 2015-06-25T21:27:27.451Z Energy_Sector_WG 2.05 1.51 2.44 126198.0 ... 218300.0 251512.0 207238.0 44274.0 CENTRAL REGION Electrified CENTRAL 1 2755.495562 POLYGON ((32.4222751926034 0.294477201111702, ... Mpigi
83 1.820025 2015-06-25T21:27:27.451Z Energy_Sector_WG MUBENDE 2015-06-25T21:27:27.451Z Energy_Sector_WG 4.05 3.62 4.06 342294.0 ... 633400.0 688819.0 641898.0 46921.0 CENTRAL REGION Electrified CENTRAL 2 2035.913289 POLYGON ((31.4568948047289 0.864583500711532, ... Mubende
84 12.957037 2015-06-25T21:27:27.451Z Energy_Sector_WG MUKONO 2015-06-25T21:27:27.451Z Energy_Sector_WG 2.10 2.41 2.91 308453.0 ... 565700.0 599817.0 437821.0 161996.0 CENTRAL REGION Electrified CENTRAL 2 14481.317116 POLYGON ((32.792320105731 0.855937709221991, 3... Mukono
85 0.000000 2015-06-25T21:27:27.451Z Energy_Sector_WG NAKAPIRIPIRIT 2015-06-25T21:27:27.451Z Energy_Sector_WG 2.24 2.71 5.20 87365.0 ... 171100.0 169691.0 166034.0 3657.0 NORTHERN REGION Electrified KARAMOJA 0.000000 POLYGON ((34.6093075268792 2.43946095397271, 3... Nakapiripirit
86 6.381238 2015-06-25T21:27:27.451Z Energy_Sector_WG NAKASEKE 2015-06-25T21:27:27.451Z Energy_Sector_WG 1.84 3.26 3.04 93607.0 ... 197500.0 197703.0 158349.0 39354.0 CENTRAL REGION Electrified CENTRAL 2 2314.272456 POLYGON ((31.9770172665494 1.46627519286954, 3... Nakaseke
87 1.511164 2015-06-25T21:27:27.451Z Energy_Sector_WG NAKASONGOLA 2015-06-25T21:27:27.451Z Energy_Sector_WG 3.00 2.01 2.99 98585.0 ... 159800.0 181863.0 157047.0 24816.0 CENTRAL REGION Electrified CENTRAL 2 507.274245 POLYGON ((32.2204633289315 1.67096759668762, 3... Nakasongola
88 0.000000 2015-06-25T21:27:27.451Z Energy_Sector_WG NAMAYINGO 2015-06-25T21:27:27.451Z Energy_Sector_WG 5.43 6.51 3.57 98443.0 ... 243700.0 223229.0 207488.0 15741.0 EASTERN REGION Under Construction EAST CENTRAL 0.000000 POLYGON ((33.9102930322431 0.462804341745947, ... Namayingo
89 0.562480 2015-06-25T21:27:27.451Z Energy_Sector_WG NAMUTUMBA 2015-06-25T21:27:27.451Z Energy_Sector_WG 2.57 2.60 3.44 121879.0 ... 224800.0 253260.0 234524.0 18736.0 EASTERN REGION Electrified EAST CENTRAL 249.186974 POLYGON ((33.7916301987549 0.9000409561864769,... Namutumba
90 0.000000 2015-06-25T21:27:27.451Z Energy_Sector_WG NAPAK 2015-06-25T21:27:27.451Z Energy_Sector_WG 2.32 9.39 2.11 76133.0 ... 209100.0 145219.0 128842.0 16377.0 NORTHERN REGION Electrified KARAMOJA 0.000000 POLYGON ((34.2835285546474 2.90633127686868, 3... Napak
91 1.967147 2015-06-25T21:27:27.451Z Energy_Sector_WG NEBBI 2015-06-25T21:27:27.451Z Energy_Sector_WG 2.50 3.10 3.08 200713.0 ... 355100.0 385220.0 327885.0 57335.0 NORTHERN REGION Electrified WEST NILE 1384.000000 POLYGON ((31.412631082672 2.76128019055835, 31... Nebbi
92 1.210302 2015-06-25T21:27:27.451Z Energy_Sector_WG NGORA 2015-06-25T21:27:27.451Z Energy_Sector_WG 2.25 4.62 2.80 74270.0 ... 164400.0 142487.0 127401.0 15086.0 EASTERN REGION Electrified TESO 325.713709 POLYGON ((33.8806678104918 1.66530780278522, 3... Ngora
93 1.586163 2015-06-25T21:27:27.451Z Energy_Sector_WG NTOROKO 2015-06-25T21:27:27.451Z Energy_Sector_WG 2.87 6.38 2.19 35348.0 ... 88400.0 66422.0 43236.0 23186.0 WESTERN REGION Electrified WESTERN 214.000000 POLYGON ((30.5504794593032 1.24256548751012, 3... Ntoroko
94 1.875473 2015-06-25T21:27:27.451Z Energy_Sector_WG NTUNGAMO 2015-06-25T21:27:27.451Z Energy_Sector_WG 3.12 1.88 2.11 319423.0 ... 491200.0 489323.0 431261.0 58062.0 WESTERN REGION Electrified SOUTH WESTERN 1882.730947 POLYGON ((30.3156523035759 -0.718001151496387,... Ntungamo
95 0.000000 2015-06-25T21:27:27.451Z Energy_Sector_WG NWOYA 2015-06-25T21:27:27.451Z Energy_Sector_WG 2.07 0.67 9.62 65195.0 ... 55500.0 130099.0 116610.0 13489.0 NORTHERN REGION Not Electrified ACHOLI 0.000000 POLYGON ((31.7532051728859 2.83498954506024, 3... Nwoya
96 0.000000 2015-06-25T21:27:27.451Z Energy_Sector_WG OTUKE 2015-06-25T21:27:27.451Z Energy_Sector_WG 2.33 3.05 4.44 65122.0 ... 88800.0 105617.0 99400.0 6217.0 NORTHERN REGION Electrified LANGO 0.000000 POLYGON ((33.4330325855648 2.63263430458564, 3... Otuke
97 0.076498 2015-06-25T21:27:27.451Z Energy_Sector_WG OYAM 2015-06-25T21:27:27.451Z Energy_Sector_WG 3.44 3.57 3.07 199307.0 ... 391900.0 388011.0 334949.0 53062.0 NORTHERN REGION Electrified LANGO 54.245595 POLYGON ((32.5698590261525 2.61576587015699, 3... Oyam
98 0.649968 2015-06-25T21:27:27.451Z Energy_Sector_WG PADER 2015-06-25T21:27:27.451Z Energy_Sector_WG 3.02 4.84 2.13 95575.0 ... 243200.0 183723.0 169643.0 14080.0 NORTHERN REGION Electrified ACHOLI 244.380527 POLYGON ((32.9243907511789 3.23790414301285, 3... Pader
99 0.882104 2015-06-25T21:27:27.451Z Energy_Sector_WG PALLISA 2015-06-25T21:27:27.451Z Energy_Sector_WG 3.12 3.70 3.43 199352.0 ... 375400.0 386074.0 353393.0 32681.0 EASTERN REGION Electrified TESO 596.276666 POLYGON ((33.7246407780626 1.34440854737872, 3... Pallisa
100 2.399119 2015-06-25T21:27:27.451Z Energy_Sector_WG RAKAI 2015-06-25T21:27:27.451Z Energy_Sector_WG 2.52 1.73 2.06 253487.0 ... 493000.0 518008.0 484163.0 33845.0 CENTRAL REGION Electrified CENTRAL 1 2562.666085 POLYGON ((31.6203770428376 -0.443995272319467,... Rakai
101 1.920666 2015-06-25T21:27:27.451Z Energy_Sector_WG RUBIRIZI 2015-06-25T21:27:27.451Z Energy_Sector_WG 3.48 2.58 1.99 76514.0 ... 126900.0 129283.0 111942.0 17341.0 WESTERN REGION Electrified SOUTH WESTERN 516.565525 POLYGON ((30.1857199988472 -0.0739225256813729... Rubirizi
102 3.014806 2015-06-25T21:27:27.451Z Energy_Sector_WG RUKUNGIRI 2015-06-25T21:27:27.451Z Energy_Sector_WG 1.02 1.53 1.27 189501.0 ... 326000.0 320567.0 284058.0 36509.0 WESTERN REGION Electrified SOUTH WESTERN 2191.575560 POLYGON ((29.8434015088105 -0.387237311395086,... Rukungiri
103 1.095850 2015-06-25T21:27:27.451Z Energy_Sector_WG SERERE 2015-06-25T21:27:27.451Z Energy_Sector_WG 3.33 5.74 3.95 145973.0 ... 309600.0 283630.0 273118.0 10512.0 EASTERN REGION Electrified TESO 302.240111 POLYGON ((33.3968510797809 1.67269443567883, 3... Serere
104 2.655048 2015-06-25T21:27:27.451Z Energy_Sector_WG SHEEMA 2015-06-25T21:27:27.451Z Energy_Sector_WG 1.26 1.40 1.34 122841.0 ... 224400.0 211720.0 153529.0 58191.0 WESTERN REGION Electrified SOUTH WESTERN 1264.205530 POLYGON ((30.4379679070966 -0.426380699028501,... Sheema
105 0.942109 2015-06-25T21:27:27.451Z Energy_Sector_WG SIRONKO 2015-06-25T21:27:27.451Z Energy_Sector_WG 1.99 1.97 2.36 131887.0 ... 245700.0 246636.0 209025.0 37611.0 EASTERN REGION Electrified ELGON 462.487278 POLYGON ((34.2991486180128 1.28935611798066, 3... Sironko
106 4.530708 2015-06-25T21:27:27.451Z Energy_Sector_WG SOROTI 2015-06-25T21:27:27.451Z Energy_Sector_WG 2.69 4.54 3.58 169819.0 ... 339300.0 297154.0 247702.0 49452.0 EASTERN REGION Electrified TESO 3176.587925 POLYGON ((33.5308898208286 2.01904942377798, 3... Soroti
107 0.725691 2015-06-25T21:27:27.451Z Energy_Sector_WG SSEMBABULE 2015-06-25T21:27:27.451Z Energy_Sector_WG 3.22 1.91 2.83 127599.0 ... 223900.0 252994.0 235970.0 17024.0 CENTRAL REGION Electrified CENTRAL 1 345.176827 POLYGON ((31.167457287809 0.233038888244143, 3... Ssembabule
108 3.387766 2015-06-25T21:27:27.451Z Energy_Sector_WG TORORO 2015-06-25T21:27:27.451Z Energy_Sector_WG 2.46 2.44 2.73 280329.0 ... 500300.0 526378.0 453841.0 72537.0 EASTERN REGION Electrified ELGON 3395.612738 POLYGON ((34.0932851934077 0.902280811530654, ... Tororo
109 34.043885 2015-06-25T21:27:27.451Z Energy_Sector_WG WAKISO 2015-06-25T21:27:27.451Z Energy_Sector_WG 4.27 4.10 6.61 1054919.0 ... 1429500.0 2007700.0 1369213.0 638487.0 CENTRAL REGION Electrified CENTRAL 1 81663.472955 POLYGON ((32.5883138617375 0.5743841921620499,... Wakiso
110 0.000000 2015-06-25T21:27:27.451Z Energy_Sector_WG YUMBE 2015-06-25T21:27:27.451Z Energy_Sector_WG 4.77 7.93 5.47 255771.0 ... 589500.0 485582.0 449976.0 35606.0 NORTHERN REGION Under Construction WEST NILE 0.000000 POLYGON ((31.2131821529133 3.79486261010287, 3... Yumbe
111 0.000000 2015-06-25T21:27:27.451Z Energy_Sector_WG ZOMBO 2015-06-25T21:27:27.451Z Energy_Sector_WG 2.47 2.16 2.93 124957.0 ... 225300.0 240368.0 194521.0 45847.0 NORTHERN REGION Electrified WEST NILE 0.000000 POLYGON ((30.9586838220449 2.71054777320169, 3... Zombo

114 rows × 29 columns


In [3]:
import os
import os.path
import copy
import ftplib
import tempfile

username = os.environ.get('NASA_USERNAME')
password = os.environ.get('NASA_PASSWORD')

def get_total_precipitation(init_date, end_date):
    """
    Get a numpy raster map of the total precipitation over the given date range in millimeters.
    """
    file_name_pattern = ('/NRTPUB/imerg/early/%(year)4d%(month)02d/' +
        '3B-HHR-E.MS.MRG.3IMERG.%(year)4d%(month)02d%(day)02d-' +
        'S%(hour)02d%(minute)02d00-E%(end_hour)02d%(end_minute)02d59.%(minute_of_day)04d.V03E.RT-H5'
    )
    precip_data = None
    iterval_minutes = 30
    count = 0
    start_date = copy.deepcopy(init_date)
    while start_date <= end_date:
        file_name = file_name_pattern % dict(
            year=start_date.year,
            month=start_date.month,
            day=start_date.day,
            hour=start_date.hour,
            minute=start_date.minute,
            end_hour=(start_date + datetime.timedelta(minutes=iterval_minutes - 1)).hour,
            end_minute=(start_date + datetime.timedelta(minutes=iterval_minutes - 1)).minute,
            minute_of_day=(
                start_date - datetime.datetime(start_date.year, start_date.month, start_date.day)
            ).total_seconds() / 60
        )
        start_date += datetime.timedelta(minutes=iterval_minutes)
        try:
            # Deleting on close causes problems because the file
            # cannot be read by hd5py if it is not closed first.
            my_tempfile = tempfile.NamedTemporaryFile(delete=False)
            ftp = ftplib.FTP("jsimpson.pps.eosdis.nasa.gov")
            ftp.login(username, password)
            ftp.retrbinary('RETR ' + file_name, my_tempfile.write)
            my_tempfile.close()
            with h5py.File(my_tempfile.name, 'r') as h5_file:
                d = np.array(h5_file['Grid/precipitationCal'])
                d = np.clip(d, 0, np.inf)
                if precip_data is None:
                    precip_data = d
                else:
                    precip_data += d
                count += 1
        except ftplib.error_perm as e:
            if str(e).startswith('550'):
                print("WARNING: File does not exist:")
                print(file_name, os.path.isfile(file_name))
                # When a file is missing, we assume the average rainfall for the rest of the
                # time iterval occured during its time period.
                # Usually a missing file is a result of choosing an interval that
                # extends beyond the downloaded data's date range, however there are
                # holes in the data. For example, there is missing data on 2016-3-12 from 12 to 3pm.
                continue
            else:
                raise e
        finally:
            os.remove(my_tempfile.name)
    #in mm/hr
    average_precipitation_rate = precip_data / count
    date_range_hours = (end_date - init_date).total_seconds() / (60 * 60)
    return average_precipitation_rate * date_range_hours

In [7]:
# An arbitrary file chosen to get coordinates for projection
file_name = "/NRTPUB/imerg/early/201611/3B-HHR-E.MS.MRG.3IMERG.20161121-S070000-E072959.0420.V03E.RT-H5"
my_tempfile = tempfile.NamedTemporaryFile(delete=False)
ftp = ftplib.FTP("jsimpson.pps.eosdis.nasa.gov")
ftp.login(username, password)
ftp.retrbinary('RETR ' + file_name, my_tempfile.write)
my_tempfile.close()
with h5py.File(my_tempfile.name, 'r') as h5_file:
    print("Keys:")
    for key in h5_file['Grid'].keys():
        print(" ", key)
    lons = np.array(h5_file['Grid/lon'])
    lats = np.array(h5_file['Grid/lat'])
os.remove(my_tempfile.name)

xmin, ymin, xmax, ymax = [lons.min(), lats.min(), lons.max(), lats.max()]
nrows, ncols = np.shape(data.transpose())
xres = (xmax - xmin) / float(ncols)
yres = (ymax - ymin) / float(nrows)
xy_to_raster_affine = Affine.from_gdal(xmin, xres, 0, ymax, 0, -yres)


Keys:
  IRkalmanFilterWeight
  HQprecipSource
  lon
  precipitationCal
  precipitationUncal
  lat
  HQprecipitation
  probabilityLiquidPrecipitation
  HQobservationTime
  randomError
  IRprecipitation

In [8]:
# TODO: Init series
timeseries = pd.read_pickle('GPM-timeseries-by-district-uganda.p')
# Create a time series of precipitation by district with ticks for each day.
for day in pd.date_range(timeseries.date.max(), pd.Timestamp.now(), freq='D'):
    data = get_total_precipitation(
        pd.to_datetime(day),
        pd.to_datetime(day) + datetime.timedelta(1))
    print(day, ", ", end='')
    zstats = zonal_stats(uganda_shapes,
        data.transpose()[::-1],
        affine=xy_to_raster_affine,
        all_touched=True)
    for idx, row in uganda_shapes.iterrows():
        zstats[idx]['district'] = row.district
        zstats[idx]['date'] = day
    zstats_frame = pd.DataFrame(zstats)
    # TODO: Add min and max
    zstats_frame = zstats_frame.rename(columns={
        'mean': 'district_mean_rainfall_mm'
    }).drop(['min', 'max', 'count'], axis=1)
    timeseries = timeseries.append(zstats_frame)

# Add computed columns, and save pickled version.
timeseries['month'] = timeseries.date.map(lambda x: pd.datetime(x.year, x.month, 1))
timeseries['week'] = timeseries.date.map(lambda x: pd.datetime(x.year, 1, 1) + pd.Timedelta(
    7 * int(
        (x - pd.datetime(x.year, 1, 1)).total_seconds() /
        (60 * 60 * 24 * 7)), 'D'))
# Arbitrary threshold
timeseries['rainy_day'] = timeseries['district_mean_rainfall_mm'] > 1.0
timeseries.to_pickle('GPM-timeseries-by-district-uganda.p.bup')
timeseries.to_pickle('GPM-timeseries-by-district-uganda.p')


.......WARNING: File does not exist:
/NRTPUB/imerg/early/201702/3B-HHR-E.MS.MRG.3IMERG.20170224-S130000-E132959.0780.V03E.RT-H5 False
WARNING: File does not exist:
/NRTPUB/imerg/early/201702/3B-HHR-E.MS.MRG.3IMERG.20170224-S133000-E135959.0810.V03E.RT-H5 False
WARNING: File does not exist:
/NRTPUB/imerg/early/201702/3B-HHR-E.MS.MRG.3IMERG.20170224-S140000-E142959.0840.V03E.RT-H5 False
WARNING: File does not exist:
/NRTPUB/imerg/early/201702/3B-HHR-E.MS.MRG.3IMERG.20170224-S143000-E145959.0870.V03E.RT-H5 False
WARNING: File does not exist:
/NRTPUB/imerg/early/201702/3B-HHR-E.MS.MRG.3IMERG.20170224-S150000-E152959.0900.V03E.RT-H5 False
WARNING: File does not exist:
/NRTPUB/imerg/early/201702/3B-HHR-E.MS.MRG.3IMERG.20170224-S153000-E155959.0930.V03E.RT-H5 False
WARNING: File does not exist:
/NRTPUB/imerg/early/201702/3B-HHR-E.MS.MRG.3IMERG.20170224-S160000-E162959.0960.V03E.RT-H5 False
WARNING: File does not exist:
/NRTPUB/imerg/early/201702/3B-HHR-E.MS.MRG.3IMERG.20170224-S163000-E165959.0990.V03E.RT-H5 False
WARNING: File does not exist:
/NRTPUB/imerg/early/201702/3B-HHR-E.MS.MRG.3IMERG.20170224-S170000-E172959.1020.V03E.RT-H5 False
WARNING: File does not exist:
/NRTPUB/imerg/early/201702/3B-HHR-E.MS.MRG.3IMERG.20170224-S173000-E175959.1050.V03E.RT-H5 False
WARNING: File does not exist:
/NRTPUB/imerg/early/201702/3B-HHR-E.MS.MRG.3IMERG.20170224-S180000-E182959.1080.V03E.RT-H5 False
WARNING: File does not exist:
/NRTPUB/imerg/early/201702/3B-HHR-E.MS.MRG.3IMERG.20170224-S183000-E185959.1110.V03E.RT-H5 False
WARNING: File does not exist:
/NRTPUB/imerg/early/201702/3B-HHR-E.MS.MRG.3IMERG.20170224-S190000-E192959.1140.V03E.RT-H5 False
WARNING: File does not exist:
/NRTPUB/imerg/early/201702/3B-HHR-E.MS.MRG.3IMERG.20170224-S193000-E195959.1170.V03E.RT-H5 False
WARNING: File does not exist:
/NRTPUB/imerg/early/201702/3B-HHR-E.MS.MRG.3IMERG.20170224-S200000-E202959.1200.V03E.RT-H5 False
WARNING: File does not exist:
/NRTPUB/imerg/early/201702/3B-HHR-E.MS.MRG.3IMERG.20170224-S203000-E205959.1230.V03E.RT-H5 False
WARNING: File does not exist:
/NRTPUB/imerg/early/201702/3B-HHR-E.MS.MRG.3IMERG.20170224-S210000-E212959.1260.V03E.RT-H5 False
WARNING: File does not exist:
/NRTPUB/imerg/early/201702/3B-HHR-E.MS.MRG.3IMERG.20170224-S213000-E215959.1290.V03E.RT-H5 False
WARNING: File does not exist:
/NRTPUB/imerg/early/201702/3B-HHR-E.MS.MRG.3IMERG.20170224-S220000-E222959.1320.V03E.RT-H5 False
WARNING: File does not exist:
/NRTPUB/imerg/early/201702/3B-HHR-E.MS.MRG.3IMERG.20170224-S223000-E225959.1350.V03E.RT-H5 False
WARNING: File does not exist:
/NRTPUB/imerg/early/201702/3B-HHR-E.MS.MRG.3IMERG.20170224-S230000-E232959.1380.V03E.RT-H5 False
WARNING: File does not exist:
/NRTPUB/imerg/early/201702/3B-HHR-E.MS.MRG.3IMERG.20170224-S233000-E235959.1410.V03E.RT-H5 False
WARNING: File does not exist:
/NRTPUB/imerg/early/201702/3B-HHR-E.MS.MRG.3IMERG.20170225-S000000-E002959.0000.V03E.RT-H5 False
.
/home/nathan/venvs/main/local/lib/python2.7/site-packages/rasterstats/io.py:291: UserWarning: Setting nodata to -999; specify nodata explicitly
  warnings.warn("Setting nodata to -999; specify nodata explicitly")

Tests/Debugging/Validation


In [65]:
# Test get_total_precipitation by comparing its output with the movie here:
# http://svs.gsfc.nasa.gov/cgi-bin/details.cgi?aid=4285
from PIL import Image, ImageDraw
def exp_normalize(arr, exp=1.0):
    """
    Convert an array to zero to one values, where one corresponds to the max value,
    then apply an exponential function to adjust the curve of the color ramp.
    """
    return (arr / arr.max()) ** exp
data = get_total_precipitation(
    datetime.datetime(2016, 11, 29),
    datetime.datetime(2016, 11, 29, 1))
im_array = np.array(exp_normalize(data.transpose()[::-1], .25) * 2 ** 8,
             dtype=np.uint8)
Image.fromarray(im_array, 'L')


Out[65]:

In [68]:
def plot_uganda_district_rainfall(raster_data):
    m = Basemap(projection='cyl',
                llcrnrlon=25, llcrnrlat=-5,
                urcrnrlon=38, urcrnrlat=10,
                resolution='l')
    fig = plt.figure(figsize=(10,10))
    # The lat/lons for pcolormesh need to be for the top-left corner
    # of the grid cell.
    dlon = (lons[-1]-lons[0]) / len(lons[1:])
    dlat = (lats[-1]-lats[0]) / len(lats[1:])
    lons2, lats2 = np.meshgrid(
        np.arange(lons[0], lons[-1] + dlon, dlon) - (dlon / 2),
        np.arange(lats[0], lats[-1] + dlat, dlat) - (dlat / 2))
    m.pcolormesh(lons2,  lats2, exp_normalize(raster_data, .1).transpose(), cmap=plt.cm.Blues, latlon=True)
    ax = plt.gca()
    patches = []
    #add polygons
    zstats = zonal_stats(uganda_shapes,
        raster_data.transpose()[::-1],
        affine=xy_to_raster_affine,
        all_touched=True)
    max_mean = max([stat['mean'] for stat in zstats])
    for idx, dist in uganda_shapes.iterrows():
        poly = dist.geometry
        stats = zstats[idx]
        if poly.geom_type == 'Polygon':
            p = PolygonPatch(poly, facecolor=plt.cm.Reds((stats['mean'] / max_mean) ** .1), alpha=.4)
            patches.append(p)
    ax.add_collection(mpl.collections.PatchCollection(patches, match_original=True))
    m.drawcoastlines()
    m.drawparallels(np.arange(-80.,80.,5.))
    m.drawmeridians(np.arange(-180.,180.,5.))
    return m

# Verify that plot_uganda_district_rainfall is accurate by using zonal_stats's underlying functions
# to draw the Uganda district shapes on a raster then plot it.
from rasterio import features
from rasterstats.io import read_features
from shapely.geometry import shape
rv_array = features.rasterize(
    [shape(feature['geometry']) for feature in read_features(uganda_shapes)],
    out_shape=data.transpose().shape,
    transform=xy_to_raster_affine,
    fill=255,
    dtype='uint8',
    all_touched=True)
plot_uganda_district_rainfall(np.array(~rv_array, dtype=np.float32)[::-1].transpose() / 255)


/home/nathan/venvs/main/local/lib/python2.7/site-packages/rasterstats/io.py:291: UserWarning: Setting nodata to -999; specify nodata explicitly
  warnings.warn("Setting nodata to -999; specify nodata explicitly")
Out[68]:
<mpl_toolkits.basemap.Basemap at 0x7f3124df1bd0>

In [69]:
# Check that the pixel at the lat/lng for the Kitale Weather Station is filled
~rv_array[::-1][(lats >= 1) & (lats <= 1.2), :][:, (lons >= 35) & (lons <= 35.2)]


Out[69]:
array([[254,   0],
       [  0,   0]], dtype=uint8)

In [70]:
# Test zone stats by sumperimposing uganda district choropleth onto rainfall raster.
# The shade of each district should correspond to the number and intensity of the
# blue rainfall pixels overlapping it.
data = get_total_precipitation(datetime.datetime(2016, 11, 29), datetime.datetime(2016, 11, 29,1))
print(plot_uganda_district_rainfall(data))
data2 = get_total_precipitation(datetime.datetime(2016, 10, 2), datetime.datetime(2016, 10, 2, 1))
print(plot_uganda_district_rainfall(data2))


<mpl_toolkits.basemap.Basemap object at 0x7f3124dfcc50>
<mpl_toolkits.basemap.Basemap object at 0x7f3124dfcc50>

In [71]:
# Highlight the basemap region on the original raster image to verify its alignment
data = get_total_precipitation(
    datetime.datetime(2016, 11, 29),
    datetime.datetime(2016, 11, 29, 1))
im_array = np.array((data / data.max())  ** .25 * 2 ** 8,
             dtype=np.uint8)
im_array[:, (lats > -5) & (lats < 10)] ^= 255
im_array[(lons > 25) & (lons < 38), :] ^= 255
Image.fromarray(im_array.transpose()[::-1], 'L')


Out[71]:

In [13]:
# Monthly rainfall comparison for Wakiso districts.
d = pd.DataFrame(timeseries)
d = d[d.district.str.startswith('Wakiso')]
d.groupby(['district', 'month']).sum().reset_index().pivot(
    index='month', columns='district', values='district_mean_rainfall_mm'
).plot(kind='bar', figsize=(12, 8))


Out[13]:
<matplotlib.axes._subplots.AxesSubplot at 0x7f71d2730450>

In [14]:
# Number of rainy days per month in Wakiso districts.
d = pd.DataFrame(timeseries)
d = d[d.district.str.startswith('Wakiso')]
d.groupby(['district', 'month']).sum().reset_index().pivot(
    index='month', columns='district', values='rainy_day'
).plot(kind='bar', figsize=(12, 8))


Out[14]:
<matplotlib.axes._subplots.AxesSubplot at 0x7f71d2585750>

In [18]:
# Daily rainfall over HUEN Weather station region.
d = pd.DataFrame(timeseries)
d = d[d.district.str.startswith('Huen')]
d.groupby(['district', 'date']).sum().reset_index().pivot(
    index='date', columns='district', values='district_mean_rainfall_mm'
).plot(figsize=(12, 8))


Out[18]:
<matplotlib.axes._subplots.AxesSubplot at 0x7f71d07d9a10>

In [19]:
# Use data from HUEN weather station in Entebbe to cross-check satalite data
# Data source: https://www.wunderground.com/history/airport/HUEN
weather_station_data = pd.read_csv("HUEN.csv", converters=dict(
    EAT=pd.to_datetime
)).set_index("EAT")
weather_station_data['month'] = weather_station_data.index.map(lambda x: "%4d-%02d" % (x.year, x.month))
weather_station_data['precipitationMm'] = weather_station_data.PrecipitationIn * 25.4
weather_station_data[['precipitationMm']].plot(kind='line', figsize=(12, 8))


Out[19]:
<matplotlib.axes._subplots.AxesSubplot at 0x7f71c9b7cc50>

In [20]:
# Montly precipitation from HUEN station
d = pd.DataFrame(weather_station_data)
d.groupby(['month']).sum()['precipitationMm'].plot(kind='bar', figsize=(12, 8))


Out[20]:
<matplotlib.axes._subplots.AxesSubplot at 0x7f71c9dc4e50>

The HUEN data shows quite a bit less precipitation than the GPM satellite, and many of the peaks do not match up.

This website shows Entebbe's average yearly rainfall. It is closer the the satellite data than the HUEN data in magnitude: https://weather-and-climate.com/average-monthly-Rainfall-Temperature-Sunshine,entebbe,Uganda

The wolfram alpha data for Entebbe precipitation does not appear to match up perfectly with the satellite data. It mentions the HUEN weather station. It is closer in magnitude to the satellite data than the weather station data: http://www.wolframalpha.com/input/?i=entebbe+rainfall+by+month


In [25]:
# Compare NOAA data for Kitale Weather station with satellite data
# source: https://www.ncdc.noaa.gov/cdo-web/orders?email=breit@ecohealthalliance.org&id=855687
kitale_station_data = pd.read_csv("kitale-weather-station.csv", converters=dict(
    DATE=pd.to_datetime)).set_index('DATE')
kitale_station_data['month'] = kitale_station_data.index.map(lambda x: pd.datetime(x.year, x.month, 1))
kitale_station_data['NOAA_precipitationMm'] = kitale_station_data.PRCP * 25.4

d = pd.DataFrame(timeseries)
d = d[d.district.str.startswith('Kitale Weather Station')]
d = d.groupby(['district', 'date']).sum().reset_index().pivot(
    index='date', columns='district', values='district_mean_rainfall_mm'
)
# Shift forward one day so measurement timespans overlap more.
# I don't know the exact time ranges of meaurement for the Kitale data,
# so I shifted the data based on what provides the maximum correlation.
kitale_station_data.index = kitale_station_data.index - pd.Timedelta("1 day")
d = d.join(kitale_station_data[['NOAA_precipitationMm']])
d.fillna(-1)[d.index > pd.datetime(2016,1,1)].plot(figsize=(12, 8))


Out[25]:
<matplotlib.axes._subplots.AxesSubplot at 0x7f71d06a7890>

Although there appears to be a large amount of disagreement between the datasets, I believe they pertain to the same region because the correlation coefficient is generally much smaller when using a different locations or shifting the timeranges.


In [26]:
d = pd.DataFrame(timeseries)
# Wakiso is arbitrarily chosen for comparison of correlation coefficients
d = d[d.district.str.startswith('Kitale Weather Station') | d.district.str.startswith('Wakiso')]
d = d.groupby(['district', 'date']).sum().reset_index().pivot(
    index='date', columns='district', values='district_mean_rainfall_mm'
)
d = d.join(kitale_station_data[['NOAA_precipitationMm']])
d[~pd.isnull(d.NOAA_precipitationMm)].corr()


Out[26]:
district Kitale Weather Station Wakiso NOAA_precipitationMm
district
Kitale Weather Station 1.000000 0.207705 0.500584
Wakiso 0.207705 1.000000 0.103760
NOAA_precipitationMm 0.500584 0.103760 1.000000

In [31]:
d = pd.DataFrame(timeseries)
req_date = "2016-11-2"
d = d[d.week == d.query("week < '%s'" % req_date).week.max()]
d = d.groupby(['district', 'week']).sum().reset_index()
with open("uganda-district-precipitation-2016-11.json", "w") as f:
    s = gp.GeoDataFrame(uganda_shapes)
    s.geometry = s.geometry.simplify(.002)
    s = s.merge(d, on="district")\
        .drop(['week'], 1)
    s['fill-color'] = '#0000FF'
    s['fill-opacity'] = s.district_mean_rainfall_mm / s.district_mean_rainfall_mm.max()
    f.write(s.to_json())