-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathplot_map.py
More file actions
280 lines (232 loc) · 11.9 KB
/
plot_map.py
File metadata and controls
280 lines (232 loc) · 11.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
import os
import numpy as np
from shapely import Polygon, MultiPolygon, unary_union
from argparse import ArgumentParser
# Internal dependencies:
from lib.plotutils import map_polygons
from lib.ioutils import (ParameterSet,
load_points,
minmax_in_polygon_file,
change_zvalue_in_polygon_file,
load_polygons,
polygons_to_file)
from lib.geoutils import select_events, convert_to_EPSG
if __name__ == "__main__":
# Read input arguments:
parser = ArgumentParser(description="Draw Voronoi diagrams or density maps from GMT ASCII tables")
parser.add_argument("configfile",
help="Configuration file")
parser.add_argument("files",
help="Grids or Polygons formatted as GMT Multiple Segment Files",
nargs='+',
type=str)
parser.add_argument('-a', '--set-transparency',
help="Set transparency index, from 0 (opaque) to 100",
type=int,
default=30)
parser.add_argument('-b', '--boundaries',
help="Plot polygons boundaries",
action='store_true')
parser.add_argument("-c", "--colormap",
help="Specify GMT colormap",
default="roma")
parser.add_argument('-e', '--overlay-events',
help="Overlay earthquakes from the catalogue which verify TMIN < T < TMAX and MAGMIN < M < MAGMAX",
nargs=4,
metavar=('MAGMIN', 'MAGMAX', 'TMIN', 'TMAX'),
default=None,
type=float)
parser.add_argument("-i", "--invert",
help="Invert colormap",
action='store_true')
parser.add_argument('-l', '--logvalues',
help="Apply log10 transformation to Z-values before plotting (linear colorscaling)",
action='store_true')
parser.add_argument('-L', '--log-colorscale',
help="Map the colorscale to Z-values using a logarithmic binning scheme",
action='store_true')
parser.add_argument('-n', '--no-coastlines-and-ticks',
help="Remove coastlines and axes ticks (useful for synthetics)",
action='store_true')
parser.add_argument('-o', '--output-directory',
help="Output directory for figures",
default=None)
parser.add_argument('-p', '--projection',
help="Specify map projection using the GMT software single-letter code (default: M for Mercator)",
default='M')
parser.add_argument("-r", "--range",
help="Specify lower and upper limits for the colormap",
nargs=2,
type=float,
default=None)
parser.add_argument("-s", "--scale-to-polygon-area",
help='Scale plotted values according to individual cell areas, using formula '
+ 'log10(10^(value) * cell_area / density_scaling_area) where variable "density_scaling_area"'
+ 'is defined in the confifguration file (note: default is 1.0 km^2)',
action='store_true')
parser.add_argument("-S", "--scale-to-reference-area",
help='Scale plotted values to an arbitrary reference area, using formula '
+ 'log10(10^(value) * ref_area / cell_area) where variable "ref_area"'
+ 'is given in argument and expressed in km^2',
type=float,
default=None)
parser.add_argument("-t", "--title",
help="Figure title")
parser.add_argument('-x', '--exclude-infinite',
help="Replace infinite Z-values by NaN to disable coloring. " +
"When option '-l' is used, replacement occurs AFTER log10 transformation",
action='store_true')
parser.add_argument('-z', '--exclude-zeros',
help="Exclude cells with null Z-values. " +
"When option '-l' is used, exclusion occurs BEFORE log10 transformation",
action='store_true')
parser.add_argument('--tight',
help="Tighten map boundaries to the bounding polygon defined in file 'bounds.txt'",
action='store_true')
parser.add_argument('--no-color',
help="Disable color-coding of polygons",
action='store_true')
args = parser.parse_args()
# Load parameters:
prms = ParameterSet()
prms.load_settings(args.configfile)
# Check output directory existence:
if args.output_directory is None:
args.output_directory = prms.figures_dir
if not os.path.isdir(args.output_directory):
os.mkdir(args.output_directory)
# Load geographical boundaries:
if prms.bounds_file is not None:
# Regular grid case:
bounding_box = load_points(prms.bounds_file) # returns a shapely.Polygon instance
else:
# Polygonal cells case:
multipol, _ = load_polygons(prms.mesh_file)
bounding_box = unary_union(multipol)
if isinstance(bounding_box, MultiPolygon):
raise Warning(f'Disjoint polygonal cells in file "{prms.mesh_file}". Please fix this.')
# Load earthquakes epicentres, if required:
if args.overlay_events is not None:
mp_epic, dates, mags, weights, uncert = load_points(prms.epicenters_file)
limits = np.array([0] + args.overlay_events)
mp_epic_sel, m_sel, t_sel, w_sel = select_events(mp_epic, mags, dates, weights, bounding_box, limits)
print(f'>> Overlay {len(mp_epic_sel.geoms)} epicenters with {limits[1]} <= M < {limits[2]} and {limits[3]} <= T <= {limits[4]}')
else:
mp_epic_sel = None
for inputfile in args.files:
print('\n' + inputfile)
prefix, ext = os.path.splitext(os.path.basename(inputfile))
tmpfiles = []
# Exclude cells with null Z-values:
if args.exclude_zeros:
print('>> Exclude polygons with zero Z-values')
# Duplicate polygon file:
tmpfiles.append(prefix + "_z.tmp")
mpoly, zvalues = load_polygons(inputfile)
in0 = (zvalues != 0.0).nonzero()[0]
zvalues = zvalues[in0]
mpoly = MultiPolygon([mpoly.geoms[i] for i in in0])
polygons_to_file(tmpfiles[-1], mpoly, zvalues)
inputfile = tmpfiles[-1]
# Apply log-transformation to Z-values:
if args.logvalues:
print('>> Compute log (base 10) of polygon Z-values')
# Duplicate polygon file:
tmpfiles.append(prefix + "_l.tmp")
_, zvalues = load_polygons(inputfile)
change_zvalue_in_polygon_file(inputfile, tmpfiles[-1], np.log10(zvalues))
inputfile = tmpfiles[-1]
# Replace infinite Z-values by NaN:
if args.exclude_infinite:
print('>> Exclude non-finite Z-values from the colorscale (set as NaN)')
_, zvalues = load_polygons(inputfile)
zvalues[np.logical_not(np.isfinite(zvalues))] = np.nan
tmpfiles.append(prefix + "_e.tmp")
change_zvalue_in_polygon_file(inputfile, tmpfiles[-1], zvalues)
inputfile = tmpfiles[-1]
# Eventually scale Z-values to individual polygons areas:
if args.scale_to_polygon_area and isinstance(args.scale_to_reference_area, float):
raise ValueError('Cannot set both options "-s" and "-S" at the same time.')
if args.scale_to_polygon_area and (args.scale_to_reference_area is None):
print('>> Divide Z-values by individual polygons areas')
pols, zvalues = load_polygons(inputfile)
pols_m = convert_to_EPSG(pols, in_epsg=prms.input_epsg, out_epsg=prms.internal_epsg)
polareas = np.array([pol.area * (prms.epsg_scaling2km ** 2) for pol in pols_m.geoms]) # in km^2
zvalues = np.log10( np.power(10, zvalues) * polareas / prms.density_scaling_factor )
tmpfiles.append(prefix + "_a.tmp")
change_zvalue_in_polygon_file(inputfile, tmpfiles[-1], zvalues)
inputfile = tmpfiles[-1]
elif isinstance(args.scale_to_reference_area, float) and (not args.scale_to_polygon_area):
ref_area = args.scale_to_reference_area
print(f'>> Scale Z-values to reference area of {ref_area} km^2')
pols, zvalues = load_polygons(inputfile)
pols_m = convert_to_EPSG(pols, in_epsg=prms.input_epsg, out_epsg=prms.internal_epsg)
polareas = np.array([pol.area * (prms.epsg_scaling2km ** 2) for pol in pols_m.geoms]) # in km^2
zvalues = np.log10(np.power(10, zvalues) * ref_area / polareas)
tmpfiles.append(prefix + "_a.tmp")
change_zvalue_in_polygon_file(inputfile, tmpfiles[-1], zvalues)
inputfile = tmpfiles[-1]
else:
print(f'>> Plot Z-values as stored in result files')
# Load colormap limits:
if args.range is None:
cmin, cmax = minmax_in_polygon_file(inputfile, exclude_inf=True)
else:
cmin = args.range[0]
cmax = args.range[1]
print(f'>> Range of Z-values: [{cmin:.3g}; {cmax:.3g}]')
# Set title:
if args.title is None:
title_arg = prefix
else:
title_arg = args.title
# Plot polygons boundaries:
if args.boundaries is True:
pen_spec = "0.5p,gray,solid"
else:
pen_spec = None
if args.no_coastlines_and_ticks:
print('>> Removes coastlines and disables axes ticks')
coast_res = None
frame = 'f'
else:
coast_res = 'i'
frame = 'af'
if args.tight:
print('>> Tighten map boundaries to the bounding box defined in bounds.txt')
boundaries = bounding_box
buffer = 0.0
else:
boundaries = None
buffer = 0.0
if args.no_color:
clim_arg = False
figure_title = title_arg # Set figure title
else:
clim_arg = [cmin, cmax]
figure_title = None # Disable figure title
map_polygons(f'{inputfile}',
clim=clim_arg,
bounds=boundaries,
pen=pen_spec,
colormap=f"{args.colormap}",
colormap_reversal=args.invert,
colormap_nbins=30,
cbar_title=f"{title_arg}",
savefig=True,
filename=os.path.join(args.output_directory, f'{prefix}.png'),
showfig=False,
coast_resolution=coast_res,
title=figure_title,
dpi=300,
spatial_buffer=buffer,
add_polygon=bounding_box,
map_projection=f"{args.projection}15c",
logscale=args.log_colorscale,
figframe=frame,
transparency_index=args.set_transparency,
add_points=mp_epic_sel)
# Delete temporary files:
if len(tmpfiles) > 0:
for file in tmpfiles:
os.remove(file)