diff options
Diffstat (limited to 'inst/gpxread.m')
-rw-r--r-- | inst/gpxread.m | 264 |
1 files changed, 264 insertions, 0 deletions
diff --git a/inst/gpxread.m b/inst/gpxread.m new file mode 100644 index 0000000..2eec30f --- /dev/null +++ b/inst/gpxread.m @@ -0,0 +1,264 @@ +## Copyright (C) 2018-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 {} {@var{out} =} gpxread (@var{fname}) +## @deftypefnx {} {@var{out} =} gpxread (@var{fname}, @dots{}) +## Read data from a gpx file. +## +## Input argument @var{fname} (a character string) can be a valid file +## name or a valid URL. +## +## If no other input arguments are given gpxread will read all data in +## @var{fname} into one output struct @var{out}. The data to be read can +## be selected and/or limited by specifying one or more of the following +## optional property/value pairs (all case-insensitive): +## +## @itemize +## @item "FeatureType' +## This option (a mere "f" suffices) can be one of: +## @table @asis +## @item +## "WayPoint or simply "w": Read waypoints. +## @end item +## +## @item +## "Track" or "t": read tracks. +## @end item +## +## @item +## "Route" or "t": read routes. +## @end item +## +## @item +## "Auto" or "a" (default value): read all data. +## @end item +## @end table +## +## Multiple FeatureType property/value pairs can be specified. +## @end item +## +## @item "Index" +## The ensuing Index value should be a numeric value, or numeric vector, +## of indices of the features to be read. +## @end item +## @end itemize +## +## Output argument @var{out} is a struct array with field names Name, +## Lat, Lon, Ele, Time, and -in case of routes- Names. "Name" refers +## to the name of the waypoints, tracks or routes that have been read. +## "Lat", "Lon" and "Ele" refer to the latitude, longitude and elevation +## of the various features, in case of tracks field "Time" refers to +## the time of the trackpoint. In case of tracks and routes these are +## vectors, each element corresponding to one track point. For each +## individual track multiple track segments are separated by NaNs. For +## routes the field "Names" contains a cell array holding the names of +## the individual route points. +## +## Examples: +## +## @example +## A = gpxread ("trip2.gpx", "feature", "track", "index", 2); +## (which returns data from the second track in file "trip1.gpx") +## @end example +## +## @example +## B = gpxread ("trip2.gpx", "f", "t", "f", "r"); +## (which returns track and route data from file "trip2.gpx") +## @end example +## +## @end deftypefn + +## Author: Philip Nienhuis <prnienhuis@users.sf.net> +## Created: 2017-02-05 + +function [outp] = gpxread (fname, varargin) + + ## Input validation + if (nargin < 1) + print_usage (); + elseif (! ischar (fname)) + error ("gpread: file name expected for arg 1\n"); + elseif (! isempty (cell2mat (cell2mat ... + (regexp (fname, '(ftp://|http://|file://)', "tokens"))))) + url = true; + else + [pth, fnm, ext] = fileparts (fname); + if (isempty (ext)) + fname = [fname ".gpx"]; + endif + url = false; + endif + + if (nargin < 2) + gtr = grt = gpt = 1; + else + gtr = grt = gpt = 0; + endif + + if (mod (nargin, 2) != 1) + error ("gpxread: insufficient nr. of input arguments\n"); + endif + + idx = []; + for ii=1:2:numel (varargin) + if (ischar (varargin{ii}) && numel (varargin{ii}) > 0) + switch lower (varargin{ii})(1) + case "f" + switch lower (varargin{ii+1})(1) + case "t" + gtr = 1; + case "r" + grt = 1; + case "w" + gpt = 1; + case "a" + gtr = grt = gpt = 1; + otherwise + error ("gpxread: unknown FeatureType '%s'", varargin{ii+1}); + endswitch + case "i" + idx = varargin{ii+1}; + if (! isnumeric (idx)) + error ("gpxread: numeric value or vector expected for arg.# %d\n", ii+1); + else + idx = uint32 (idx); + endif + otherwise + warning ("gpxread: unknown option '%s' - ignored\n", varargin{ii}); + endswitch + else + error ("gpxread: wrong argument type for arg. %d - 'FeatureType' or \ +'Index' expected", ii+1); + endif + endfor + + if (url) + ## Untested + xml = urlread (fname); + else + fid = fopen (fname, "r"); + if (fid < 0) + error ("gpxread: couldn't open file %s\n", fname); + endif + xml = fread (fid, Inf, "char=>char")'; + fclose (fid); + endif + + outp = repmat (struct ("Type", "-", ... + "Lat", [], ... + "Lon", [], ... + "Ele", [], ... + "Time", [], ... + "Name", "-"), 0, 1); + + if (gpt) + ## (Try to) Read waypoints + ptrnp = 'wpt lat="(.*?)" lon="(.*?)">.*?ele>(.*?)</ele.*?name>(.*?)</name'; + wpts = reshape (cell2mat ( regexp (xml, ptrnp, "tokens")'), [], 4); + if (! isempty (wpts)) + wpts(:, 1:3) = num2cell (str2double (wpts(:, 1:3))); + if (isempty (idx)) + idx = [1:size(wpts, 1)]'; + else + idx(idx < 1) = []; + idx(idx > size (wpts, 1)) = []; + endif + [outp(1:numel (idx)).Name] = deal (wpts(idx, 4){:}); + [outp(1:numel (idx)).Type] = deal ("WayPoint"); + [outp(1:numel (idx)).Lat] = deal (wpts(idx, 1){:}); + [outp(1:numel (idx)).Lon] = deal (wpts(idx, 2){:}); + [outp(1:numel (idx)).Ele] = deal (wpts(idx, 3){:}); + endif + endif + + if (gtr) + ## Read tracks + ptrnt1A = '<trkpt lat="(.*?)" lon="(.*?)".*?ele>(.*?)</ele>.*?<time>(.*?)</time'; + ptrnt1B = '<trkpt lat="(.*?)" lon="(.*?)".*?time>(.*?)</time.*?ele>(.*?)</ele>'; + ptrnt2 = '<trkpt lat="(.*?)" lon="(.*?)".*?ele>(.*?)</ele'; + [trk, ~, is] = getxmlnode (xml, "trk", 1, 1); + itr = 0; + if (is != 0) + do + if (isempty (idx) || ismember (++itr, idx)) + [trkseg, ~, ist] = getxmlnode (trk, "trkseg", 1, 1); + if (ist != 0) + outp(end+1).Type = "Track"; + outp(end).Name = getxmlnode (trk, "name", 1, 1); + ## Check if tracks have time node + it = index (trkseg, "</time>", "first"); + ## Check ele/time node order + if (it) + if (index (trkseg, "</ele>", "first") < it) + ptrnt = ptrnt1A; + dcol = 4; + else + ptrnt = ptrnt1B; + dcol = 3; + endif + ncols = 4; + else + ptrnt = ptrnt2; + ncols = 3; + endif + out = NaN (0, ncols); + do + arr = reshape (cell2mat (regexp (trkseg, ptrnt, "tokens")), ncols, [])'; + if (ncols == 4) + arr = [ (str2double (arr(:, [1:2 7-dcol]))) (datenum (arr(:, dcol), "yyyy-mm-ddTHH:MM:SSZ")) ]; + else + arr = str2double (arr); + endif + out = [out; arr; NaN(1, ncols)]; + [trkseg, ~, ist] = getxmlnode (trk, "trkseg", ist, 1); + until (ist == 0); + out(end, :) = []; + outp(end).Lat = out(:, 1); + outp(end).Lon = out(:, 2); + outp(end).Ele = out(:, 3); + if (ncols == 4) + outp(end).Time = out(:, 4); + endif + endif + endif + [trk, ~, is] = getxmlnode (xml, "trk", is, 1); + until (is == 0); + endif + endif + + if (grt) + ## Read routes + ptrnr = 'rtept lat="(.*?)" lon="(.*?)".*?ele>(.*?)</ele.*?name>(.*?)</name'; + [rte, ~, is] = getxmlnode (xml, "rte", 1, 1); + irt = 0; + if (is != 0) + do + if (isempty (idx) || ismember (++irt, idx)) + outp(end+1).Type = "Route"; + outp(end).Name = getxmlnode (rte, "name", 1, 1); + [rteseg, ~, isr] = getxmlnode (rte, "rtept", 1, 1); + rtp = reshape (cell2mat ( regexp (rte, ptrnr, "tokens")'), [], 4); + [outp(end).Lat] = str2double (rtp(:, 1)); + [outp(end).Lon] = str2double (rtp(:, 2)); + [outp(end).Ele] = str2double (rtp(:, 3)); + [outp(end).Names] = rtp(:, 4); + endif + [rte, ~, is] = getxmlnode (xml, "rte", is, 1); + until (is == 0); + endif + endif + +endfunction |