-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
3d81ec9
commit dc45f49
Showing
2 changed files
with
215 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
#!/bin/bash | ||
module load python | ||
|
||
GROUP="FIPS" | ||
ZOOM="NO" | ||
BAYAREA="NO" | ||
for i in "$@"; do | ||
case $i in | ||
-c=*|--censusMap=*) | ||
CENSUS="${i#*=}" | ||
shift # past argument=value | ||
;; | ||
-i=*|--infectionCases=*) | ||
INFECTION="${i#*=}" | ||
shift # past argument=value | ||
;; | ||
-m=*|--plotMode=*) #CUML/DAILY | ||
OUTPUT_MODE="${i#*=}" | ||
shift # past argument=value | ||
;; | ||
-n=*|--normalize=*) #RAW (no normalization), POP (normalize to population) | ||
NORM="${i#*=}" | ||
shift # past argument=value | ||
;; | ||
-g=*|--group=*) #group data to a higher level (FIPS and GROUP are 2 options for now) | ||
GROUP="${i#*=}" | ||
shift # past argument=value | ||
;; | ||
-z=*|--zoomin=*) #zoom in towards a location, format: "startTime, stopTime, target x, target y, zoomFactor" | ||
ZOOM="${i#*=}" | ||
shift # past argument=value | ||
;; | ||
-b=*|--bayAreaDataFormat=*) | ||
BAYAREA="${i#*=}" | ||
shift # past argument=value | ||
;; | ||
-*|--*) | ||
echo "Unknown option $i" | ||
exit 1 | ||
;; | ||
*) | ||
;; | ||
esac | ||
done | ||
|
||
echo "PATH TO INFECTION CASES: ${INFECTION}" | ||
echo "PATH TO CENSUS MAP: ${CENSUS}" | ||
echo "MODE TO PLOT: ${OUTPUT_MODE}" | ||
echo "NORMALIZE TO: ${NORM}" | ||
echo "GROUP TO (DEFAULT: FIPS): ${GROUP}" | ||
echo "ZOOM IN (DEFAULT: NO): ${ZOOM}" | ||
echo "BAY AREA DATA FORMAT (DEFAULT: NO): ${BAYAREA}" | ||
|
||
#plot everyday's cases on a cencus map | ||
python3 plotCases.py ${INFECTION} ${CENSUS} ${OUTPUT_MODE} ${NORM} ${GROUP} ${ZOOM} ${BAYAREA} | ||
|
||
#create movie | ||
ffmpeg -framerate 1 -i "casefile%05d.png" -c:v libx264 -r 30 -pix_fmt yuv420p movie.mp4 | ||
|
||
#remove temporary files | ||
rm *.png |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,154 @@ | ||
import matplotlib as mpl | ||
import matplotlib.pyplot as plt | ||
from mpl_toolkits.axes_grid1.inset_locator import inset_axes | ||
from matplotlib import colors | ||
from matplotlib import cm | ||
import os | ||
import fiona | ||
import geopandas as geopd | ||
import pysal as ps | ||
import shapefile | ||
import pandas as pd | ||
from shapely.geometry import shape | ||
import numpy as np | ||
import os | ||
import sys | ||
import re | ||
|
||
argList= sys.argv[1:] | ||
zoom="" | ||
group="" | ||
BayAreaDataFormat="" | ||
if(len(argList)<5): #5 is the minimum mandatory arguments | ||
print("Some mandatory argument is missing") | ||
exit | ||
if len(argList)>= 1: infCasesDir= argList[0]+"/" | ||
if len(argList)>= 2: CensusDir = argList[1]+"/" | ||
if len(argList)>= 3: outputMetric = argList[2] | ||
if len(argList)>= 4: datanorm = argList[3] | ||
if len(argList)>= 5: group = argList[4] | ||
if len(argList)>= 6: zoom = argList[5] #format: "startTime, stopTime, target x, target y, zoomFactor" | ||
if len(argList)>= 7: BayAreaDataFormat= argList[6] | ||
|
||
gdf = geopd.GeoDataFrame(columns=["STATEFP00", "COUNTYFP00", "TRACTCE00", "CTIDFP00", "NAME00", "NAMELSAD00", "MTFCC00", "FUNCSTAT00", "ALAND00", "AWATER00", "INTPTLAT00", "INTPTLON00", "geometry"]) | ||
#for Bay Area dataset, use the following format: | ||
if(BayAreaDataFormat=="YES"): gdf = geopd.GeoDataFrame(columns=["geoid", "name_", "namelsad", "mtfcc", "funcstat", "aland", "awater", "intptlat", "intptlon", "SHAPE_Leng", "SHAPE_Area"]) | ||
|
||
infCasesFiles= [os.path.join(infCasesDir, f) for f in os.listdir(infCasesDir) if f.endswith(".csv")] | ||
|
||
for file in os.listdir(CensusDir): | ||
filename = CensusDir + file + "/" + file | ||
myshp = open(filename+".shp", "rb") | ||
mydbf = open(filename+".dbf", "rb") | ||
myprj = open(filename+".prj", "rb") | ||
r = shapefile.Reader(shp=myshp, dbf=mydbf, prj=myprj) | ||
attributes, geometry = [], [] | ||
field_names = [field[0] for field in r.fields[1:]] | ||
for row in r.shapeRecords(): | ||
geometry.append(shape(row.shape.__geo_interface__)) | ||
attributes.append(dict(zip(field_names, row.record))) | ||
df = geopd.GeoDataFrame(data = attributes, geometry = geometry) | ||
gdf= gdf._append(df) | ||
r.close() | ||
|
||
if(BayAreaDataFormat=="NO"): gdf['fips'] = gdf["CTIDFP00"].astype(int) | ||
else: gdf['fips'] = gdf["geoid"].astype(int) | ||
gdf["county"]= gdf["fips"]//1000000 | ||
gdf0= gdf.copy() | ||
gdf1= gdf.copy() | ||
gdf0['per'] = 0.0 | ||
gdf1['per'] = 0.0 | ||
gdf1.set_index('fips',inplace=True) | ||
|
||
|
||
sorted_infCasesFiles= sorted(infCasesFiles, key=lambda x: int( re.findall('\d+', x)[0])) | ||
for caseFile in sorted_infCasesFiles: | ||
idx = int(re.findall('\d+', caseFile)[0]) | ||
print("processing file", caseFile) | ||
df000 = pd.read_csv(caseFile, header=None, names=["fips", "per"], dtype={"fips":int, "per":float}) | ||
#df000.set_index("fips",inplace=True) | ||
gdf_join = pd.merge(gdf,df000, left_on="fips", right_on="fips", how='inner') | ||
if(group=="COUNTY"): | ||
if(datanorm=="RAW"): | ||
df000["county"]= df000["fips"]//1000000 | ||
df000.drop("fips", inplace=True, axis=1) | ||
df000= df000.groupby("county").sum() | ||
gdf_join = pd.merge(gdf,df000, left_on='county', right_on='county', how='inner') | ||
#gdf_join.set_index('fips',inplace=True) | ||
columns= gdf1.columns.values | ||
columns= columns[:-1].tolist() #exclude per | ||
if(outputMetric=="DAILY"): | ||
gdf1= pd.concat([gdf0, gdf_join]).groupby(columns).sum().reset_index() | ||
if(outputMetric=="CUML"): | ||
gdf1= pd.concat([gdf0, gdf_join]).groupby(columns).sum().reset_index() | ||
|
||
fig, ax = plt.subplots(figsize=(10,10)) | ||
|
||
gdfplot= geopd.GeoDataFrame(gdf1) | ||
#use cmap with a norm to convert a number to RGB | ||
#two slope norm | ||
#set center to 0, adjust the negative vmin to control how fast color changes from 0 to vmax | ||
if(datanorm=="RAW"): | ||
vmin = -1.0 | ||
vmax = 25 | ||
norm = colors.TwoSlopeNorm(vmin=vmin, vcenter=0.000, vmax=vmax) | ||
cmap = cm.cool | ||
sm = plt.cm.ScalarMappable(cmap=cmap, norm=norm) | ||
gdfplot["per"] = gdfplot["per"] + 1 #avoid log(0) | ||
gdfplot["per"] = np.log2(gdfplot["per"]) | ||
plt.colorbar(sm, ax=ax, ticks=np.linspace(0, 25, 6), label="Log2 Cumulative Cases", orientation="vertical") | ||
else: | ||
if(datanorm=="POP"): | ||
vmin = -10.0 | ||
vmax = 1.0 | ||
norm = colors.TwoSlopeNorm(vmin=vmin, vcenter=0.000, vmax=vmax) | ||
cmap = cm.cool | ||
sm = plt.cm.ScalarMappable(cmap=cmap, norm=norm) | ||
plt.colorbar(sm, ax=ax, ticks=np.linspace(0, 1, 11), label="Cumulative Cases per Population", orientation="vertical") | ||
else: | ||
print("INPUT METRIC NOT PROVIDED", norm) | ||
exit | ||
ax.xaxis.set_ticks_position('top') | ||
ax.set(aspect='equal', xticks=[], yticks=[]) | ||
|
||
gdfplot.plot(column= 'per', ax = ax, norm=norm, cmap=cmap, lw=3)#, edgecolor='black',)#, legend=False) | ||
|
||
#adjust the position and zoom in | ||
if(zoom!="NO"): | ||
startTime, stopTime, targetX, targetY, zoomFactor= zoom.split(',') | ||
startTime= int(startTime) | ||
stopTime= int(stopTime) | ||
shiftTime=10 #we need 10 days to smoothly shift to the target position | ||
targetX= float(targetX) | ||
targetY= float(targetY) | ||
zoomFactor= float(zoomFactor) | ||
xrate= (0.5-targetX)/10 | ||
yrate= (0.5-targetY)/10 | ||
zoomrate= (1.0-1.0/zoomFactor)/(stopTime-shiftTime-startTime) | ||
|
||
ylim = ax.get_ylim() | ||
xlim = ax.get_xlim() | ||
if(idx>=startTime and idx<(startTime+shiftTime)): | ||
#gradually shift to the interested local area | ||
ax.set_xlim((xlim[0]-(xlim[1]-xlim[0])*xrate*(idx-startTime), xlim[1]-(xlim[1]-xlim[0])*xrate*(idx-startTime))) | ||
ax.set_ylim((ylim[0]-(ylim[1]-ylim[0])*yrate*(idx-startTime), ylim[1]-(ylim[1]-ylim[0])*yrate*(idx-startTime))) | ||
if(idx>=(startTime+shiftTime) and idx<stopTime): | ||
#now all figures start looking at the interested local area | ||
ax.set_xlim((xlim[0]-(xlim[1]-xlim[0])*xrate*shiftTime, xlim[1]-(xlim[1]-xlim[0])*xrate*shiftTime) ) | ||
ax.set_ylim((ylim[0]-(ylim[1]-ylim[0])*yrate*shiftTime, ylim[1]-(ylim[1]-ylim[0])*yrate*shiftTime) ) | ||
#we want to zoom in a little more | ||
ylim = ax.get_ylim() | ||
xlim = ax.get_xlim() | ||
ax.set_xlim((xlim[0]+(xlim[1]-xlim[0])*zoomrate*(idx-startTime-shiftTime)/2, xlim[1]-(xlim[1]-xlim[0])*zoomrate*(idx-startTime-shiftTime)/2) ) | ||
ax.set_ylim((ylim[0]+(ylim[1]-ylim[0])*zoomrate*(idx-startTime-shiftTime)/2, ylim[1]-(ylim[1]-ylim[0])*zoomrate*(idx-startTime-shiftTime)/2) ) | ||
if(idx>=stopTime): | ||
#stay exactly where last figure was. No more shifting, no more zooming | ||
ax.set_xlim((xlim[0]-(xlim[1]-xlim[0])*xrate*shiftTime, xlim[1]-(xlim[1]-xlim[0])*xrate*shiftTime) ) | ||
ax.set_ylim((ylim[0]-(ylim[1]-ylim[0])*yrate*shiftTime, ylim[1]-(ylim[1]-ylim[0])*yrate*shiftTime) ) | ||
ylim = ax.get_ylim() | ||
xlim = ax.get_xlim() | ||
ax.set_xlim((xlim[0]+(xlim[1]-xlim[0])*zoomrate*(stopTime-shiftTime-startTime)/2, xlim[1]-(xlim[1]-xlim[0])*zoomrate*(stopTime-shiftTime-startTime)/2 )) | ||
ax.set_ylim((ylim[0]+(ylim[1]-ylim[0])*zoomrate*(stopTime-shiftTime-startTime)/2, ylim[1]-(ylim[1]-ylim[0])*zoomrate*(stopTime-shiftTime-startTime)/2 )) | ||
output= "casefile"+str(idx+1).zfill(5)+".png" | ||
plt.savefig(output) | ||
plt.close() |