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
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
|
## Copyright (C) 2014-2020 Philip Nienhuis
##
## This program is free software; you can redistribute it and/or modify it
## under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 3 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program. If not, see <http://www.gnu.org/licenses/>.
## -*- texinfo -*-
## @deftypefn {Function File} @var{H} = mapshow (@var{data})
## @deftypefnx {Function File} @var{H} = mapshow (@var{data}, @var{clr})
## @deftypefnx {Function File} @var{H} = mapshow (@var{data}, @var{clr}, ...)
## @deftypefnx {Function File} @var{H} = mapshow (@var{data}, ...)
## @deftypefnx {Function File} @var{H} = mapshow (@var{X}, @var{Y})
## @deftypefnx {Function File} @var{H} = mapshow (@var{X}, @var{Y}, @var{clr})
## Draw a map based on raster or shapefile data.
##
## @var{data} can be:
##
## @itemize
## @item The filename of a GIS raster file (any file supported by the GDAL
## library) or of an ArcGIS shapefile. mapshow will invoke rasterread.m
## and rasterdraw.m.
##
## @item A raster band struct created by rasterread.m; in that case the
## corresponding raster info struct (also made by rasterread.m) is required
## as second input argument.
##
## @item A struct created by shaperead.m. @var{data} can be a mapstruct or
## an Octave-style shape struct.
##
## @item The base name or full file name of an ArcGis shape file. mapshow
## will invoke shaperead.m and shapedraw.m
## @end itemize
##
## If the first two arguments to mapshow.m contain numeric vectors, mapshow
## will simply draw the vectors as XY lines. The vectors can contain NaNs (in
## identical positions) to separate parts.
##
## For raster maps currently no further input arguments are recognized.
## For shapefile data, optional argument @var{clr} can be a predefined color
## ("k", "c", etc.), and RGB triplet, or a 2 X 1 column vector of predefined
## colors or RGB triplets (each row containing a predefined color or triplet).
## The upper row will be used for points and lines, the lower row for solid
## shape features. For XY data, only the first row is used. One-character
## color codes can be preceded by one-character linestyle indicators
## (":", "-", "--", "-.") to modify the linestyle for polylines, or marker
## styles ("o", "*", ".", "+", "@", ">", "<", "s", "d", "h", "v", "^") for
## points.
##
## Any other arguments are considered graphics properties for (multi-)points,
## polylines and polygons and will be conveyed as-is to the actual plotting
## routines.
##
## Additionally, if the first argument is a shape struct, mapshow accepts a
## property-value pair "symbolspec" (minimum abbreviation "symb") with a value
## comprising a cell array containing instructions on how to display the
## shape contents. Multiple sympolspec property/value pairs can be specified.
##
## Return argument @var{h} is a handle to the plot figure.
##
##
## Examples:
##
## @example
## H = mapshow ("/full/path/to/map")
## (draws a raster map and returns the figure handle in H)
## @end example
##
## @example
## H = mapshow ("shape.shp", ":g")
## H = mapshow ("shape.shp", "color", "g", "linestyle", ":")
## (draws a polygon shapefile "shape.shp" with green
## dotted lines and return figure handle in H)
## @end example
##
## @example
## mapshow (X, Y, "k")
## (plot vectors X and Y in black color)
## @end example
##
## @example
## mapshow (X, Y, "-.r", "linewidth", 5)
## (plot vectors X and Y as a dashdotted thick red line)
## @end example
##
## @example
## mapshow (data, "symbolspec", symsp1, "symb", symsp2)
## (draw contents of shapestruct (or mapstruct) data
## according to the symbolspecs symsp1 and symsp2)
## @end example
##
## @seealso{geoshow, shapedraw, shapeinfo, shaperead, shapewrite, makesymbolspec, rasterread, rasterdraw, rasterinfo}
## @end deftypefn
## Author: Philip Nienhuis <prnienhuis@users.sf.net>
## Created: 2014-11-17 after a suggestion by Carnë Draug
## Updates:
## 2014-2015 many many fixes
## 2015-02-04 Relax check for mapstructs; don't test BoundingBox (Point shapes)
## 2015-02-11 Fix minor syntax error in do_symspecs rule check
function h = mapshow (varargin)
## Check "hold" state; creates a new figure if none was present
if (ishold())
old_holdstate = "on";
else
old_holdstate = "off";
endif
hold on;
if ischar (varargin{1})
[~, ~, ext] = fileparts (varargin{1});
if (strncmpi (ext, ".shp", 4))
## Directly plot shapefile. Strip away symbolspecs
isym = find (strncmpi ("symbolspec", varargin(2:end), 4));
if (! isempty (isym))
warning ("mapshow.m: ignoring symbolspecs when drawing .shp directly");
endif
varargin(isym:isym+1) = [];
h = shapedraw (varargin{:});
else
## Assume any raster file
h = rasterdraw (varargin{1});
endif
elseif (nargin > 1 && israster (varargin{1}) && isstruct (varargin{2}))
## Assume raster structs
h = rasterdraw (varargin{:});
elseif (isshape (varargin{1}))
## Assume a shape struct or a shape file name. Get optional symbolspec
isym = find (strncmpi("symbolspec", varargin(2:end), 4));
if (! isempty (isym))
## FIXME move this stanze into a separate subfunction
## Found symbolspec. Check if it can be invoked
if (ischar (varargin{1}))
## Symbolspec not applicable to shape file argument
error ("mapshow: symbolspec not supported when plotting shape files directly\n");
endif
## First get & check geometry
symspecs = {varargin{isym+2}};
h = do_symspecs (varargin{1}, symspecs, old_holdstate);
else
## No symbolspec to process; plot shape(file) directly
endif
elseif (nargin >= 2 && isvector (varargin{1}) && isvector (varargin{2}))
## Assume args #1 & #2 are lines. Find optional color argument.
if (nargin >= 3)
if (ischar (varargin{3}))
## Assume arg #3 is a valid color name or character like 'b'
h = plot (varargin{1}, varargin{2}, varargin{3});
elseif (isnumeric (varargin{3} && size (varargin{3}, 2) == 3))
## Assume arg#3 is a valid color triplet
h = plot (varargin{1}, varargin{2}, "color", varargin{3});
else
error ("mapshow.m: color argment expected for arg. #3\n");
endif
axis equal;
else
## Plot args #1 & #2 without further ado
h = plot (varargin{1}, varargin{2}, "color", [0.6 0.6 0.6]);
endif
else
error ("mapshow: only plotting of shapes or vector data is implemented\n");
endif
## Reset hold state
hold (old_holdstate);
axis equal;
if (! nargout)
h = 0;
endif
endfunction
##--------------------------------------------------------------------------
## Copyright (C) 2014,2015 Philip Nienhuis <prnienhuis@users.sf.net>
function retval = isshape (s)
retval = false;
## Check if s is a recognized shape file struct; just a brief check
if (isstruct (s))
## Yep. Find out what type
fldn = fieldnames (s);
if (all (ismember ({"vals", "shpbox"}, fldn)))
## Assume it is an Octave-style struct read by shaperead
retval = 2;
elseif (all (ismember ({"Geometry", "X"}, fldn)))
## Assume it is a Matlab-style mapstruct
retval = 1;
endif
endif
endfunction
##--------------------------------------------------------------------------
## Copyright (C) 2015 Philip Nienhuis <prnienhuis@users.sf.net>
function retval = israster (s)
retval = 0;
## Check if s is a recognized raster struct; just a brief check
if (isstruct (s))
## Yep. Find out what type
fldn = fieldnames (s);
if (ismember ("data", fldn) && ismatrix (s.data))
## Assume it is an Octave-style struct read by shaperead
retval = 1;
endif
endif
endfunction
##--------------------------------------------------------------------------
## Copyright (C) 2015 Philip Nienhuis <prnienhuis@users.sf.net>
##
## Process symbolspecs one by one
function h = do_symspecs (shp, symspecs)
for jj=1:numel (symspecs);
symspec = symspecs{jj};
geom = lower (symspec {1});
if (! ischar (geom))
error ("mapshow: char argument expected for Geometry field in symbolspec\n");
elseif (! ismember (lower (geom), {"point", "multipoint", "line", ...
"polyline", "polygon", "patch"}))
error ("mapshow: unknown Geometry: %s in symbolspec\n", geom);
endif
for ii=2:numel (symspec)
rule = symspec{ii};
if (! ischar (rule{1}))
warning ("mapshow: char string expected for attribute of rule %d in symbolspec\n", ii-1);
endif
## Get property or attribute
if (strcmpi (rule{1}, "default"))
## Rule applies to all shape features with geom. Plot shape features
if (isshape (shp) == 1)
## mapstruct shapes are allowed to be heterogeneous
gdx = find (strcmpi (geom, {shp.Geometry}));
h = shapedraw (shp(gdx), rule(3:end){:});
elseif (isshape (shp) == 2)
h = shapedraw (shp, rule(3:end){:});
else
error ("mapshow: improper shape struct type\n");
end
elseif (isshape (shp) == 1)
## ML mapstruct type shape struct. Apply to proper geometry features
gdx = find (strcmpi (geom, {shp.Geometry}));
## Rule applies to one specific attribute. Check if it exists
if (ismember (rule{1}, fieldnames (shp)))
## Try to apply rule. We need try-catch to catch non-matching classes
if (islogical (rule{2}))
## Find attributes that are true. FIXME catches zero attributes too
try
idx = find ([shp(gdx).(rule{1})]);
catch
warning ("mapshow: rule %d not applicable to atribute %s\n", ...
ii-1, rule{1});
end_try_catch
elseif (isnumeric (rule{2}))
## Check which shape features have attribute values in the range
minr = min (rule{2});
maxr = max (rule{2});
try
idx = find ([shp(gdx).(rule{1})] >= minr & ...
[shp(gdx).(rule{1})] <= maxr);
catch
warning ("mapshow: rule %d not applicable to atribute %s\n", ...
ii-1, rule{1});
end_try_catch
elseif (ischar (rule{2}))
## Match strings
try
idx = find (strcmp (rule{2}, {shp(gdx).(rule{1})}));
catch
warning ("mapshow: rule %d not applicable to atribute %s\n", ...
ii-1, rule{1});
end_try_catch
endif
## Plot shape features
h = shapedraw (shp(gdx(idx)), rule(3:end){:});
else
## Attribute not found
warning ("mapshow: attribute '%s' in rule #%d not found\n", rule(ii){1});
endif
elseif (isshape (shp) == 2)
## Oct-style shp struct. Prepare indexing into coords
shp.idx = [ shp.idx; (size(shp.vals, 1) + 2) ];
## Some fields are not explicitly in the shape
switch rule{1}
## Coordinates
case "X"
shp_field = shp.vals(:, 1);
case "Y"
shp_field = shp.vals(:, 2);
case "Z"
shp_field = shp.vals(:, 3);
case "M"
## Measure
shp_field = shp.vals(:, 4);
case "npt"
## Nr. or points/vertices
shp_field = shp.npt;
case "npr"
## Nr. of parts
try
shp_field = cellfun (@numel, shp.npr);
catch
warning ("mapshow: rule %d attribute 'npr' not found\n", ii-1);
shp_field = NaN;
end_try_catch
#case "bbox"
# shp_field = shp.bbox;
otherwise
## "Regular" attributes
if (ismember (rule{1}, fieldnames(shp)))
shp_field = shp.(rule{1});
else
warning ("mapshow: rule %d attribute '%s' not found\n", ...
ii-1, rule{1});
endif
endswitch
## Try to apply rule. We need try-catch to catch non-matching classes
if (islogical (rule{2}))
## Find attributes that are true. FIXME catches zero attributes too
try
idx = find (shp_field);
catch
warning ("mapshow: rule %d not applicable to atribute %s\n", ...
ii-1, rule{1});
end_try_catch
elseif (isnumeric (shp_field))
## Check which shape features have attribute values in the range
minr = min (rule{2});
maxr = max (rule{2});
if (ismember (rule{1}, {"X", "Y", "Z", "M"}))
## Multiple attribute values per shape feature (polylines, ...)
idx = [];
try
for jj=1:numel (shp.idx)
## Include all polylines/-gons/multipatches with at least
## one value in the range
idx = [idx; (any ( ...
shp_field(shp.idx(jj:shp.idx(jj+1)-2)) >= minr & ...
shp_field(shp.idx(jj:shp.idx(jj+1)-2)) <= maxr)) ];
endfor
catch
warning ("mapshow: rule %d not applicable to atribute %s\n", ...
ii-1, rule{1});
end_try_catch
else
## Single attribute values per shape feature
try
idx = find (shp_field >= minr & ...
shp_field <= maxr);
catch
warning ("mapshow: rule %d not applicable to atribute %s\n", ...
ii-1, rule{1});
end_try_catch
endif
elseif (iscellstr (shp_field))
## Match strings
try
idx = find (strcmp (rule{2}, shp_field));
catch
warning ("mapshow: rule %d not applicable to atribute %s\n", ...
ii-1, rule{1});
end_try_catch
endif
## Plot shape features; but first set up struct
vals = zeros(0, 6);
jdx = [];
for jj=1:numel (idx)
jdx = [ jdx ; (size (vals, 1) + 1) ];
vals = [ vals; ...
shp.vals(shp.idx(idx(jj)):shp.idx(idx(jj)+1)-2, :); ...
NaN(1, 6) ];
endfor
vals(end, :) = [];
sct.shpbox = shp.shpbox;
sct.vals = vals;
sct.bbox = shp.bbox(idx, :);
sct.npt = shp.npt(idx);
sct.npr = shp.npr(idx);
sct.idx = jdx;
sct.(rule{1}) = shp.(rule{1})(idx);
## Draw results
h = shapedraw (sct, rule(3:end){:});
else
## Unrecognized shape type
error ("mapshow: improper shape struct type\n");
endif
endfor
endfor
endfunction
|