librasteve / raku-Physics-Measure

do calculations on objects with value, units and error
Artistic License 2.0
11 stars 2 forks source link

Minute angle parsing error #63

Open bazzaar opened 11 months ago

bazzaar commented 11 months ago

First of all, many thanks for making this module :-)

Tried to assign :

use Physics::Measure :ALL;

my $θ1 = ♎️<30′30″>;

say $θ1;

resulting in the following errors, also tried my $θ1 = ♎️<2′>; with similar errors too.

Not a big issue as my $θ1 = ♎️<0°2′0″>; does parse, however it would be nice if just minutes, or even just seconds would parse on their own.

My use case was to create a 1st pass set of control points at a defined grid (2' spacing) over an map image to be geo-(located/rectified), the points file will be imported into GRASS GIS project and individual points repositioned in the georectify GUI.

bazzaar:~/Documents/raku> raku angle_test.raku

WARNING: unhandled Failure detected in DESTROY. If you meant to ignore it, you can mark it as handled by calling .Bool, .so, .not, or .defined methods. The Failure was:
Cannot convert string to number: trailing characters after number in '30⏏′3' (indicated by ⏏)
  in regex number at /home/bazzaar/.raku/sources/AC2EDA86048961DC766A619C1031B5676390A32A (Physics::Measure) line 29
  in regex  at /home/bazzaar/.raku/sources/AC2EDA86048961DC766A619C1031B5676390A32A (Physics::Measure) line 115
  in regex  at /home/bazzaar/.raku/precomp/680007B646B98C4376F3879D4994BF61D6C52F69/AC/AC2EDA86048961DC766A619C1031B5676390A32A line 1
  in method defn-extract at /home/bazzaar/.raku/sources/AC2EDA86048961DC766A619C1031B5676390A32A (Physics::Measure) line 115
  in method new at /home/bazzaar/.raku/sources/AC2EDA86048961DC766A619C1031B5676390A32A (Physics::Measure) line 66
  in sub prefix:<♎️> at /home/bazzaar/.raku/sources/AC2EDA86048961DC766A619C1031B5676390A32A (Physics::Measure) line 582
  in block <unit> at angle_test.raku line 3

WARNING: unhandled Failure detected in DESTROY. If you meant to ignore it, you can mark it as handled by calling .Bool, .so, .not, or .defined methods. The Failure was:
Cannot convert string to number: trailing characters after number in '30⏏′30' (indicated by ⏏)
  in regex number at /home/bazzaar/.raku/sources/AC2EDA86048961DC766A619C1031B5676390A32A (Physics::Measure) line 29
  in regex  at /home/bazzaar/.raku/sources/AC2EDA86048961DC766A619C1031B5676390A32A (Physics::Measure) line 115
  in regex  at /home/bazzaar/.raku/precomp/680007B646B98C4376F3879D4994BF61D6C52F69/AC/AC2EDA86048961DC766A619C1031B5676390A32A line 1
  in method defn-extract at /home/bazzaar/.raku/sources/AC2EDA86048961DC766A619C1031B5676390A32A (Physics::Measure) line 115
  in method new at /home/bazzaar/.raku/sources/AC2EDA86048961DC766A619C1031B5676390A32A (Physics::Measure) line 66
  in sub prefix:<♎️> at /home/bazzaar/.raku/sources/AC2EDA86048961DC766A619C1031B5676390A32A (Physics::Measure) line 582
  in block <unit> at angle_test.raku line 3

WARNING: unhandled Failure detected in DESTROY. If you meant to ignore it, you can mark it as handled by calling .Bool, .so, .not, or .defined methods. The Failure was:
Cannot convert string to number: trailing characters after number in '30⏏′30″' (indicated by ⏏)
  in regex number at /home/bazzaar/.raku/sources/AC2EDA86048961DC766A619C1031B5676390A32A (Physics::Measure) line 29
  in regex  at /home/bazzaar/.raku/sources/AC2EDA86048961DC766A619C1031B5676390A32A (Physics::Measure) line 115
  in regex  at /home/bazzaar/.raku/precomp/680007B646B98C4376F3879D4994BF61D6C52F69/AC/AC2EDA86048961DC766A619C1031B5676390A32A line 1
  in method defn-extract at /home/bazzaar/.raku/sources/AC2EDA86048961DC766A619C1031B5676390A32A (Physics::Measure) line 115
  in method new at /home/bazzaar/.raku/sources/AC2EDA86048961DC766A619C1031B5676390A32A (Physics::Measure) line 66
  in sub prefix:<♎️> at /home/bazzaar/.raku/sources/AC2EDA86048961DC766A619C1031B5676390A32A (Physics::Measure) line 582
  in block <unit> at angle_test.raku line 3

WARNING: unhandled Failure detected in DESTROY. If you meant to ignore it, you can mark it as handled by calling .Bool, .so, .not, or .defined methods. The Failure was:
Cannot convert string to number: trailing characters after number in '30⏏′' (indicated by ⏏)
  in regex number at /home/bazzaar/.raku/sources/AC2EDA86048961DC766A619C1031B5676390A32A (Physics::Measure) line 29
  in regex  at /home/bazzaar/.raku/sources/AC2EDA86048961DC766A619C1031B5676390A32A (Physics::Measure) line 115
  in regex  at /home/bazzaar/.raku/precomp/680007B646B98C4376F3879D4994BF61D6C52F69/AC/AC2EDA86048961DC766A619C1031B5676390A32A line 1
  in method defn-extract at /home/bazzaar/.raku/sources/AC2EDA86048961DC766A619C1031B5676390A32A (Physics::Measure) line 115
  in method new at /home/bazzaar/.raku/sources/AC2EDA86048961DC766A619C1031B5676390A32A (Physics::Measure) line 66
  in sub prefix:<♎️> at /home/bazzaar/.raku/sources/AC2EDA86048961DC766A619C1031B5676390A32A (Physics::Measure) line 582
  in block <unit> at angle_test.raku line 3

Couldn't parse defn Str ′30″
  in sub CreateUnit at /home/bazzaar/.raku/sources/AFFDACB323F74A1CEE5D5EBD82AC6AD7D8DDA3BA (Physics::Unit) line 536
  in method new at /home/bazzaar/.raku/sources/AFFDACB323F74A1CEE5D5EBD82AC6AD7D8DDA3BA (Physics::Unit) line 93
  in sub GetUnit at /home/bazzaar/.raku/sources/AFFDACB323F74A1CEE5D5EBD82AC6AD7D8DDA3BA (Physics::Unit) line 350
  in method new at /home/bazzaar/.raku/sources/AC2EDA86048961DC766A619C1031B5676390A32A (Physics::Measure) line 67
  in sub prefix:<♎️> at /home/bazzaar/.raku/sources/AC2EDA86048961DC766A619C1031B5676390A32A (Physics::Measure) line 582
  in block <unit> at angle_test.raku line 3
bazzaar commented 11 months ago

Perhaps this grammar code could be a help, it parses angles defined by just minutes, seconds or both, and it also parses proj-string's which use different quote characters, used by PROJ utility cs2cs for example. It also incorporates E-W, N-S indicator for Latitude/Longitude, and allows for fractional seconds. It also allows for parsing of simple DMS strings without geographic info. It's a bit clunky with the real number parsing, and I'm sure it can be improved on. I guess it'll need associated action method code to facilitate easy extract from the Match object, and to sort out types?

Also, it's convention to always have the coordinates in a set order, X-Y, or Longitude-Latitude. Longitudes with a West indicator will convert to negative decimal degrees.

grammar DMS_parser {

    token TOP   { <point>||<coord> }

    token point { <coord> <.ws>? <coord> }
    token coord { <group>[<longitude>||<latitude>]? }
    token group { <term>+ }
    token term  { <num> [<degree>||<minute>||<second>] }
    token real  { <int> '.' <int> }
    token num   { <minus>? <real>||<int> }
    token minus { '-' }
    token int   { \d+ }

    proto token longitude {*}
          token longitude:sym<E> { <sym> }
          token longitude:sym<e> { <sym> }
          token longitude:sym<W> { <sym> }
          token longitude:sym<w> { <sym> }

    proto token latitude {*}
          token latitude:sym<N>  { <sym> }
          token latitude:sym<n>  { <sym> }
          token latitude:sym<S>  { <sym> }
          token latitude:sym<s>  { <sym> }

    proto token degree {*}
          # <°> is U+00B0 ('\x[0080]')
          token degree:sym<°> { <sym> }
          # <d> is U+0064 ('\x[0064]')
          token degree:sym<d> { <sym> }

    proto token minute {*}
          # <′> is U+2032 ('\x[2032]')
          token minute:sym<′> { <sym> }
          # <'> is U+0027 ('\x[0027]')
          token minute:sym<'> { <sym> }

    proto token second {*}
          # <″> is U+2033 ('\x[2033]')
          token second:sym<″> { <sym> }
          # <"> is U+2022 ('\x[2022]')
          token second:sym<"> { <sym> }
}

say DMS_parser.parse(<4°>);
say DMS_parser.parse(<45°>);
say DMS_parser.parse(<45°30′>);
say DMS_parser.parse(<45°30′20″>);
say DMS_parser.parse(<45°30′20.546″>);
say DMS_parser.parse(<3′>);
say DMS_parser.parse(<30′>);
say DMS_parser.parse(<30′2″>);
say DMS_parser.parse(<30′20″>);
say DMS_parser.parse(<30′20.546″>);
say DMS_parser.parse(<2″>);
say DMS_parser.parse(<20″>);
say DMS_parser.parse(<20.546″>);

# examples of proj_string's
say DMS_parser.parse(<121d13'52.432"W 37d28'57.767"N>);
say DMS_parser.parse(<2d20'55"E48d51'12"N>);

The above code is free to use as anyone sees fit.

librasteve commented 11 months ago

Hi @bazzaar - this is very nice - thanks for the suggestion and the code example. The errors you show are explained by the fact that Physics::Measure does not support the partial variants that you want.

Sadly, I am not able to commit any tuits to this module at the moment. Let's leave this as an open issue for now and I will see what I can make of it later.

Alternatively, you could consider making a new raku module that overrides a standard Physics::Measure::Angle object with this behaviour?

There is some precedence for this in my Physics::Navigation module. class NavAngle is Angle {...} Also this module has the notion of Latitude, Longitude and Position objects. That way you can adapt the print side also... but still benefit from the "unit math" that Physics::Measure / Physics::Unit offers (AngularSpeed = Angle/Duration, SolidAngle = Angle^2).

My superficial look at PROJ suggests that there is a gap in the market for a raku PROJ capability and tbh my Physics::Navigation module is not intended to be a core Geo library, rather a precursor to exploring raku capabilities around my Yacht::Navigation hobby and so does not have (eg) Google Maps or GPS formats (and tests). So I would imagine that a common interface to import / export to/from Physics::Measure <=> "Geo::PROJ" would be suitable for most use cases.