/* --------------------------------------------------------------- */ /* deg2dec -- check a degrees notation and convert to decimal */ /* --------------------------------------------------------------- */ /* */ /* Copyright (c) Mike Cowlishaw, 2010, 2012. All rights reserved. */ /* */ /* Permission to use, copy, modify, and/or distribute this */ /* software for any purpose with or without fee is hereby granted, */ /* provided that the above copyright notice and this permission */ /* notice appear in all copies, and that notice and the date of */ /* any modifications be added to the software. */ /* */ /* This software is provided "as is". No warranties, whether */ /* express, implied, or statutory, including, but not limited to, */ /* implied warranties of merchantability and fitness for a */ /* particular purpose apply to this software. The author shall */ /* not, in any circumstances, be liable for special, incidental, */ /* or consequential damages, for any reason whatsoever. */ /* */ /* --------------------------------------------------------------- */ /* This program checks a degree coordinate string, with optional */ /* type restrictions. */ /* */ /* arg1 is coord, the string to be parsed */ /* arg2 is type, the caseless indicator of restrictions: */ /* 'lat' -- magnitude must be <=90, prefixes NSns+- OK */ /* 'lon' -- magnitude must be <=180, prefixes EWew+- OK */ /* '' -- magnitude must be <=1E+6; prefixes +- OK */ /* */ /* returns converted decimal degrees, or '' if bad syntax */ /* */ /* when called as a command the result is displayed as well as */ /* returned, and also the arg1 type can be added to arg1 with a */ /* comma, for example: */ /* */ /* deg2dec N52:25:17, lat */ /* */ /* The syntax allowed for coord is [p]d[:m[:s]] where: */ /* [] indicates optional parts */ /* p (prefix) is a single character, permitted according to type */ /* d (degrees) is at least one digit, value as per type */ /* m (minutes) is such that 0 <= m < 60 */ /* s (seconds) is such that 0 <= s < 60 */ /* No embedded blanks are permitted. */ /* The final number in the sequence (only) may have a fractional */ /* part. e.g., +23.123 N52:25:17.33 (there must be a digit */ /* preceding the dot). */ /* --------------------------------------------------------------- */ signal on novalue arg coord, type junk if type='' then parse var coord coord ',' type junk -- command form coord=strip(coord) type=strip(type) -- prefs is such that the first two represent negative sign select when junk\='' then return final('') when type='LAT' then do; max=90; prefs='-S+N'; end when type='LON' then do; max=180; prefs='-W+E'; end when type='' then do; max=1E+6; prefs='--++'; end otherwise return final('') end -- handle prefix if pos(' ', coord)>0 then return final('') parse var coord c1 +1 rest sign='+' if datatype(c1, 'n') then nop -- no prefix else do -- first char not a number s=pos(c1, prefs) if s=0 then return final('') -- invalid if s<=2 then sign='-' coord=rest end if right(coord, 1)=':' then return final('') -- trailing : parse var coord deg ':' rest if \datatype(deg, 'n') then return final('') if deg>max then return final('') if left(deg, 1)='.' then return final('') if rest='' then return final(deg) -- minutes to come if verify(deg, '0123456789')>0 then return final('') -- sign, e, E, or '.'? parse var rest min ':' sec if \datatype(min, 'n') then return final('') if min>=60 then return final('') if left(min, 1)='.' then return final('') if sec='' then return final(deg+(min/60)) -- seconds to come if verify(min, '0123456789')>0 then return final('') -- sign, e, E, or '.'? if verify(sec, '0123456789.')>0 then return final('') -- sign, e, or E? if \datatype(sec, 'n') then return final('') if sec>=60 then return final('') if left(sec, 1)='.' then return final('') return final(deg+(min/60)+(sec/3600)) /* final -- a result to return */ final: procedure expose sign parse arg val if val\='' then val=sign||val parse source . how . if how\='COMMAND' then return val if val='' then say '*** Bad syntax in arument(s)' else say val return val