Skip to content

Commit e4eb1c1

Browse files
authored
Merge pull request #665 from rjleveque/dtopo_contours2kmz
add kmltools.dtopo_contours2kmz
2 parents ba00e9f + 6421f9e commit e4eb1c1

File tree

1 file changed

+156
-19
lines changed

1 file changed

+156
-19
lines changed

src/python/geoclaw/kmltools.py

Lines changed: 156 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
- kml_build_colorbar - create a colorbar to display on GE
2929
- topo2kmz - create kmz file showing onshore and offshore topography
3030
- transect2kml - create kml file showing a set of points on a transect
31+
- dtopo_contours2kmz - create a kmz file containing contour plots of dtopo(s)
3132
- kml_header - used internally
3233
- kml_footer - used internally
3334
- kml_region - used internally
@@ -41,6 +42,11 @@
4142
except:
4243
pass # assume python2, already has reload
4344

45+
import numpy as np
46+
from numpy import ma
47+
from matplotlib import pyplot as plt
48+
import os
49+
4450
def f2s(x, num_digits=6):
4551
r"""
4652
Convert float to string in fixed point notation with at most
@@ -954,6 +960,11 @@ def fgmax2kml(rundata=None,fname='fgmax_grids.kml',verbose=True,combined=False):
954960
xy = ([fg.x1,fg.x2,fg.x3,fg.x4],
955961
[fg.y1,fg.y2,fg.y3,fg.y4])
956962
poly2kml(xy, kml_file, fname_root, color='8888FF')
963+
elif fg.point_style==4:
964+
# points specified by mask in a topo-like file, so plot its extent:
965+
topo_file_name = fg.xy_fname
966+
topo_type = 3
967+
topo2kml(topo_file_name, topo_type, color='8888FF')
957968
else:
958969
print('fgmax2kml not yet implemented for point_style = %i' \
959970
% fg.point_style)
@@ -1006,7 +1017,7 @@ def fgout2kml(rundata=None,fname='fgout_grids.kml',verbose=True,combined=False):
10061017
box2kml(xy, kml_file, fname_root, color='8888FF')
10071018

10081019

1009-
def make_input_data_kmls(rundata=None, combined=False):
1020+
def make_input_data_kmls(rundata=None, combined=False, dtopo_contours=False):
10101021
"""
10111022
Produce kml files for the computational domain, all gauges and regions
10121023
specified, and all topo and dtopo files specified in rundata.
@@ -1050,6 +1061,13 @@ def make_input_data_kmls(rundata=None, combined=False):
10501061
dtopo_file_name = f[-1]
10511062
dtopo_type = f[0]
10521063
dtopo2kml(dtopo_file_name, dtopo_type)
1064+
if dtopo_contours:
1065+
# also make a kmz file showing dtopo contours (takes a bit longer)
1066+
fname_kmz = os.path.split(dtopo_file_name)[-1]
1067+
fname_kmz = os.path.splitext(fname_kmz)[0] # w/o path or extension
1068+
fname_kmz = fname_kmz + '_contours.kmz'
1069+
dtopo_contours2kmz(dtopo_file_name, dtopo_type=dtopo_type,
1070+
dZ_interval=1, fname_kmz=fname_kmz,verbose=False)
10531071

10541072

10551073
def pcolorcells_for_kml(X, Y, Z, png_filename=None, dpc=2, max_inches=15.,
@@ -1090,25 +1108,27 @@ def pcolorcells_for_kml(X, Y, Z, png_filename=None, dpc=2, max_inches=15.,
10901108
png file, as described below.
10911109
10921110
Internally the value `dpi` (dots per inch) for the png file is
1093-
determined so that it is at least 16 and so that:
1111+
determined so that it is at least 16 and so that::
1112+
10941113
dpi * x_inches = dcp * x_cells
10951114
dpi * y_inches = dcp * y_cells
1115+
10961116
where `x_cells`, `y_cells` are the number of cells in each direction.
10971117
10981118
`kwargs` are passed to `pcolormesh`, e.g. `cmap` and `norm` are
10991119
generally specified.
11001120
11011121
This function returns `fig, ax, png_extent, kml_dpi` so the user can further
11021122
annotate the figure befor saving it as a png file, which should then
1103-
be done with:
1123+
be done with::
1124+
11041125
plt.savefig(png_filename, transparent=True, dpi=kml_dpi)
1126+
11051127
The `png_extent` is needed in construcing a kml file to display the
11061128
png file on Google Earth, e.g. using the function `png2kml` in this
11071129
module.
11081130
"""
11091131

1110-
from matplotlib import pyplot as plt
1111-
import numpy as np
11121132

11131133
# If X is 2d extract proper 1d slice:
11141134
if X.ndim == 1:
@@ -1160,8 +1180,8 @@ def pcolorcells_for_kml(X, Y, Z, png_filename=None, dpc=2, max_inches=15.,
11601180
dots_per_cell = dpc
11611181
max_dots = dots_per_cell * max_cells
11621182

1163-
# determine dots per inch for png file, minimum 16:
1164-
kml_dpi = max(int(round(max_dots / max_inches)), 16)
1183+
# determine dots per inch for png file, minimum 64:
1184+
kml_dpi = max(int(round(max_dots / max_inches)), 64)
11651185
dots_x = x_cells * dots_per_cell
11661186
dots_y = y_cells * dots_per_cell
11671187

@@ -1347,9 +1367,7 @@ def kml_build_colorbar(cb_filename, cmap, cmin=None, cmax=None,
13471367
cmin, cmax are used only if nrm is not provided.
13481368
"""
13491369

1350-
import matplotlib.pyplot as plt
13511370
import matplotlib as mpl
1352-
import numpy
13531371

13541372
fig = plt.figure(figsize=(1.2,3))
13551373
ax1 = fig.add_axes([0.3, 0.075, 0.20, 0.80])
@@ -1366,12 +1384,12 @@ def kml_build_colorbar(cb_filename, cmap, cmin=None, cmax=None,
13661384

13671385
# make sure ticks appear at lower and upper limits of scale:
13681386
cbticks = cb1.get_ticks()
1369-
cbticks = numpy.hstack([norm.vmin, cbticks, norm.vmax])
1387+
cbticks = np.hstack([norm.vmin, cbticks, norm.vmax])
13701388
# remove 2nd and/or next-to-last tick if they are cramped:
13711389
if cbticks[1]-cbticks[0] < 0.25*(cbticks[2]-cbticks[1]):
1372-
cbticks = numpy.hstack((cbticks[:1], cbticks[2:]))
1390+
cbticks = np.hstack((cbticks[:1], cbticks[2:]))
13731391
if cbticks[-1]-cbticks[-2] < 0.25*(cbticks[-2]-cbticks[-3]):
1374-
cbticks = numpy.hstack((cbticks[:-2], cbticks[-1:]))
1392+
cbticks = np.hstack((cbticks[:-2], cbticks[-1:]))
13751393
cb1.set_ticks(cbticks)
13761394

13771395
if label:
@@ -1408,18 +1426,15 @@ def topo2kmz(topo, zlim=(-20,20), mask_outside_zlim=True, sea_level=0.,
14081426
- *close_figs* to close the pyplot figures after making png files
14091427
14101428
:Future:
1411-
If `force_dry` is an array of the same shape as `topo.Z` then another png
1412-
and kml file are created for land that is below `sea_level` but where
1413-
`force_dry = True`.
1429+
- If `force_dry` is an array of the same shape as `topo.Z` then another png
1430+
and kml file are created for land that is below `sea_level` but where
1431+
`force_dry = True`.
14141432
14151433
"""
14161434

14171435
import os, glob
1418-
import numpy
1419-
from numpy import ma
14201436
from clawpack.visclaw import colormaps
14211437
import zipfile
1422-
import matplotlib.pyplot as plt
14231438

14241439
assert force_dry is None, 'force_dry not yet implemented'
14251440

@@ -1440,7 +1455,7 @@ def topo2kmz(topo, zlim=(-20,20), mask_outside_zlim=True, sea_level=0.,
14401455
data_break=sea_level)
14411456

14421457
if mask_outside_zlim:
1443-
Z = ma.masked_where(numpy.logical_or(topo.Z<zlim[0], topo.Z>zlim[1]), topo.Z)
1458+
Z = ma.masked_where(np.logical_or(topo.Z<zlim[0], topo.Z>zlim[1]), topo.Z)
14441459
cbar_extend = 'neither'
14451460
else:
14461461
Z = topo.Z
@@ -1549,3 +1564,125 @@ def transect2kml(xtrans, ytrans, fname='transect.kml'):
15491564
kml_file.write("\n</Document>\n</kml>")
15501565

15511566
print('Created ', fname)
1567+
1568+
def dtopo_contours2kmz(dtopofiles, dtopo_type=3, dZ_interval=1, dZmax=40,
1569+
text_label=True, text_x=None, text_y=None,
1570+
fname_kmz=None, verbose=True):
1571+
1572+
"""
1573+
Create dtopo_contours.kmz file containing contour plots of the dtopo
1574+
deformations with radio buttons to select which one to show.
1575+
(Or dtopofiles can be a string for a single dtopofile.)
1576+
1577+
To show multiple dtopofiles, they must all have the same extent.
1578+
1579+
:Inputs:
1580+
1581+
- *dtopofiles* (str or list of str): single or list of dtopofile paths
1582+
- *dZ_interval* (float) the interval in meters between contours.
1583+
- *dZmax* (float) max for contour levels shown (and -dZmax is the min)
1584+
- *text_label* Text label to add to plots,
1585+
If text_label is a string this will be added as a text label,
1586+
If text_label == True a standard label will be added with the
1587+
dopofile name, dZ_interval, and the max, min values of dZ.
1588+
- *text_x, text_y* are the location of the text,
1589+
or if None then the mean of dtopofile.X and dtopofile.Y are used.
1590+
1591+
:Future:
1592+
- Could also add pcolor plot of deformation to kmz file.
1593+
"""
1594+
from clawpack.geoclaw import dtopotools
1595+
1596+
import os,sys, zipfile, shutil
1597+
1598+
kml_dir = 'temp_dtopos_kml'
1599+
os.system('mkdir -p %s' % kml_dir)
1600+
events = []
1601+
png_files = []
1602+
1603+
if type(dtopofiles) == str:
1604+
dtopofiles = [dtopofiles] # if only one passed in
1605+
1606+
previous_png_extent = None
1607+
1608+
for dtopofile in dtopofiles:
1609+
dtopofile = os.path.abspath(dtopofile)
1610+
event = os.path.splitext(os.path.split(dtopofile)[-1])[0]
1611+
events.append(event)
1612+
if verbose:
1613+
print('Making contours for event = ',event)
1614+
print(' contour interval = %gm' % dZ_interval)
1615+
1616+
dtopo = dtopotools.DTopography(dtopofile, dtopo_type=dtopo_type)
1617+
1618+
# first make empty plot of right dimensions:
1619+
Zm = ma.masked_where(dtopo.Y < 90, dtopo.Y) # all masked
1620+
fig,ax,png_extent,kml_dpi = pcolorcells_for_kml(dtopo.X, dtopo.Y, Zm,
1621+
png_filename=None, dpc=4,
1622+
verbose=verbose)
1623+
1624+
if previous_png_extent is not None:
1625+
assert png_extent == previous_png_extent, \
1626+
'*** dtopofiles have different extents\n' \
1627+
+ '*** cannot make a single kmz file displaying them'
1628+
previous_png_extent = png_extent
1629+
1630+
# now add contour plot:
1631+
clines = np.arange(dZ_interval,dZmax,dZ_interval)
1632+
lw = 2.
1633+
ax.contour(dtopo.X, dtopo.Y, dtopo.dZ[-1,:,:], clines, colors='r',
1634+
linestyles='-', linewidths=lw)
1635+
clines = np.arange(-dZmax,0,dZ_interval)
1636+
ax.contour(dtopo.X, dtopo.Y, dtopo.dZ[-1,:,:], clines, colors='c',
1637+
linestyles='-', linewidths=lw)
1638+
1639+
if text_label:
1640+
if text_x is None:
1641+
text_x = dtopo.x.mean()
1642+
if text_y is None:
1643+
text_y = dtopo.y.mean()
1644+
1645+
dZ_min = dtopo.dZ[-1,:,:].min()
1646+
dZ_max = dtopo.dZ[-1,:,:].max()
1647+
if type(text_label) == str:
1648+
plt.text(text_x, text_y, text_label, fontsize=15,color='yellow')
1649+
else:
1650+
plt.text(text_x, text_y,'%s\ndZ_min = %.1fm, dZ_max = %.1fm' \
1651+
% (event,dZ_min,dZ_max) \
1652+
+ '\ndZ_interval = %.1f m' % dZ_interval, \
1653+
fontsize=15,color='yellow')
1654+
1655+
png_filename = '%s_contours.png' % event
1656+
png_files.append(png_filename)
1657+
png_filename = os.path.join(kml_dir, png_filename)
1658+
plt.savefig(png_filename, transparent=True, dpi=kml_dpi)
1659+
plt.close(fig)
1660+
1661+
if fname_kmz is None:
1662+
fname_kmz = 'dtopo_contours.kmz'
1663+
path_kmz = os.path.abspath('./%s' % fname_kmz) # path in this directory
1664+
1665+
# move to kml_dir for making kml files:
1666+
savedir = os.getcwd()
1667+
os.chdir(kml_dir)
1668+
png_names = events
1669+
fname = 'dtopo_contours.kml'
1670+
name = 'dtopo_contours'
1671+
png2kml(png_extent, png_files=png_files, png_names=png_names,
1672+
radio_style=True, name=name, fname=fname, verbose=verbose)
1673+
1674+
files = [fname] + ['%s_contours.png' % event for event in events]
1675+
if 0:
1676+
print('kmz file will include:')
1677+
for file in files:
1678+
print(' ', file)
1679+
1680+
with zipfile.ZipFile(path_kmz, 'w') as zip:
1681+
for file in files:
1682+
zip.write(file)
1683+
print('Created %s' % fname_kmz)
1684+
os.chdir(savedir)
1685+
1686+
if verbose:
1687+
print('Removing ',kml_dir)
1688+
shutil.rmtree(kml_dir)

0 commit comments

Comments
 (0)