http://qs1969.pair.com?node_id=11149884


in reply to Re: approximating geological problems with highway data
in thread approximating geological problems with highway data

I can't see your code because Gitlab...

Nuts. I apologize for the dry link. I don't mean to be coy about posting source, but rather more mindful of the initial S in SSCCE. It's not punitively long:

Source:

#!/usr/bin/perl use v5.030; # strictness implied use warnings; use feature qw[ signatures ]; no warnings qw[ experimental::signatures ]; use Data::Dumper; use DateTime; use DateTime::Format::ISO8601; use DateTime::TimeZone; use Log::Log4perl; use Try::Tiny; our $debug = 0; my $logger = init_log(); my $ref_events = init_event(); my @events = @$ref_events; my $pi = atan2( 1, 1 ) * 4; $logger->info("pi is $pi "); my $redrock = 5200; #Willsey thumbnail for Bonneville Trail elevati +on $logger->info("Bonneville max altitude in feet: $redrock"); ## event loop # Initial previous event to something my ( $prev_lat, $prev_long ) = ( 42, -112 ); #some point in the snake river plain for my $event (@events) { my $epoch = parse_event($event); ## determine altitude die unless exists $event->{location}; # restriction my $return = get_elevation_from_coordinates( $event->{location}->{la +t}, $event->{location}->{lon}, $debug ); $logger->info( " $event->{name} $event->{location}->{lat} $event->{location}->{l +on}"); $logger->info("return from the google is $return meters"); my $feet = 3.28084 * $return; $logger->info("Altitude in feet is $feet"); my $diff = $redrock - $feet; $logger->info("Difference from max Bonneville elevation is $diff ft" +); $logger->info("=============="); my $d = distance( $prev_lat, $prev_long, $event->{location}->{lat}, $event->{location}->{lon}, "M" ); $logger->info("distance is $d miles"); $logger->info("=============="); # rotate values $prev_lat = $event->{location}->{lat}; $prev_long = $event->{location}->{lon}; } ## end event loop sub init_log { { package Log::Log4perl; sub get_logger ($pkg) { return bless [], $pkg; } sub show (@arg) { warn @arg, "\n"; } sub debug ( $ignore, @rest ) { show( 'DEBUG: ', @rest ); } sub info ( $ignore, @rest ) { show( 'INFO: ', @rest ); } sub warn ( $ignore, @rest ) { show( 'WARNING: ', @rest ); } sub error ( $ignore, @rest ) { die 'ERROR: ', @rest, "\n"; } } my $logger2 = Log::Log4perl->get_logger(); $logger2->info("$0"); return $logger2; } sub init_event { my $date_str = "2021-10-14"; my $time_str = "03:22:31"; my @events = ( { "name" => "Boise", "date" => $date_str, "time" => $time_str, "location" => { lon => -116.2, lat => 43.61 }, }, { "name" => "near sublett", "date" => $date_str, "time" => $time_str, "location" => { lon => -113.2104084, lat => 42.3278114 } }, { "name" => "snowville", "date" => $date_str, "time" => $time_str, "location" => { lon => -112.7105234, lat => 41.9655701 }, 42.152429, -112.9842191 }, { "name" => "juniper", "date" => $date_str, "time" => $time_str, "location" => { lon => -112.9842191, lat => 42.152429 } }, { "name" => "on the outcrop", "date" => $date_str, "time" => $time_str, "location" => { lon => -111.7849951, lat => 40.703684, }, }, { "name" => "Cascade way", "date" => $date_str, "time" => $time_str, "location" => { lon => -111.7941259, lat => 40.7062734 }, }, { "name" => "Mantua", "date" => $date_str, "time" => $time_str, "location" => { lon => -111.944728, lat => 41.5073303 }, }, { "name" => "dry creek", "date" => $date_str, "time" => $time_str, "location" => { lon => -111.9537977, lat => 41.5501001 }, }, { "name" => "wellsville", "date" => $date_str, "time" => $time_str, "location" => { lon => -111.9288947, lat => 41.6365147 }, }, ); return \@events; } sub event2str { my $event = shift; if ( !exists $event->{_is_parsed} ) { warn "event has not been parsed, just dumping it..."; print Dump($event); } my $str = $event->{name} . " on " . $event->{datetime} . " (" . $event->{datetime}->epoch . " seconds unix-epoch)" . " timezone: " . $event->{datetime}->time_zone->name; if ( exists $event->{location} ) { if ( ref( $event->{location} ) eq 'HASH' ) { $str .= " (lat: " . $event->{location}->{lat} . ", lon: " . $event->{location}->{lon} . ")"; } else { $str .= "(" . $event->{location} . ")" } } return $str; } sub parse_event { my $event = shift; $debug //= 0; if ( !exists $event->{date} ) { die "date field is missing from even +t." } my $datestr = $event->{date}; die "event does not have a 'name' field, please specify one, anythin +g really." unless exists $event->{name}; my $timestr = "00:00:01"; if ( exists $event->{time} ) { $timestr = $event->{time}; print "event2epoch(): setting time to '$timestr' ...\n" if $debug > 0; die "time '$timestr' is not valid, it must be in the form 'hh:mm:s +s'." unless $timestr =~ /^\d{2}:\d{2}:\d{2}$/; } else { $event->{time} = $timestr } my $isostr = $datestr . 'T' . $timestr; my $dt = DateTime::Format::ISO8601->parse_datetime($isostr); die "failed to parse date '$isostr', check date and time fields." unless defined $dt; $event->{datetime} = $dt; my $tzstr = 'UTC'; if ( exists $event->{timezone} ) { $tzstr = $event->{timezone}; print "event2epoch(): found a timezone via 'timezone' field as '$tzstr' (tha +t does not mean it is valid) ...\n" if $debug > 0; } elsif ( exists $event->{location} ) { my $loc = $event->{location}; if ( ( ref($loc) eq '' ) && ( $loc =~ /^[a-zA-Z]$/ ) ) { # we have a location string my @alltzs = DateTime::TimeZone->all_names; my $tzstr; for (@alltzs) { if ( $_ =~ /$loc/i ) { $tzstr = $_; last } } die "event's location can not be converted to a timezone, consider specify +ing the 'timezone' directly or setting 'location' coordinates with: \ +[lat,lon\]." unless $tzstr; print "event2epoch(): setting timezone via 'location' name to '$timestr' ... +\n" if $debug > 0; } elsif ( ( ref($loc) eq 'HASH' ) && ( exists $loc->{lat} ) && ( exists $loc->{lon} ) ) { # we have a [lat,lon] array for location require Geo::Location::TimeZone; my $gltzobj = Geo::Location::TimeZone->new(); $tzstr = $gltzobj->lookup( lat => $loc->{lat}, lon => $loc->{lon +} ); if ( !$tzstr ) { die "timezone lookup from location coordinates lat:" . $loc->{lat} . ", lon:" . $loc->{lon} . " has failed."; } print "event2epoch(): setting timezone via 'location' coordinate +s lat:" . $loc->{lat} . ", lon:" . $loc->{lon} . " ...\n" if $debug > 0; } } if ($tzstr) { print "event2epoch(): deduced timezone to '$tzstr' and setting it +...\n" if $debug > 0; try { $dt->set_time_zone($tzstr) } catch { die "$_\n failed to set the timezone '$tzstr', is it valid?" } } $event->{_is_parsed} = 1; $event->{epoch} = $dt->epoch; return $event->{epoch}; } sub get_elevation_from_coordinates { my ( $lat, $lon, $debug ) = @_; use LWP::UserAgent; use HTTP::Request; use Data::Roundtrip; $debug //= 0; my $ua = LWP::UserAgent->new( agent => 'Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Firefox +/78.0', ); my $response; my $payload = 'latitude=' . $lat . '&longitude=' . $lon . '&application_max_assets_mtime=1559625591'; my $payloadlen = length($payload); # this request was translated from Curl command-line # by [Corion]'s https://corion.net/curl2lwp.psgi my $req = HTTP::Request->new( 'POST' => 'https://www.mapcoordinates.net/admin/component/edit/Vpc_MapCoordinate +s_Advanced_GoogleMapCoords_Component/Component/json-get-elevation', [ 'Connection' => 'keep-alive', 'Accept' => '*/*', 'Accept-Encoding' => 'gzip, x-gzip, deflate, x-bzip2, bzip2', 'Accept-Language' => 'en-US,en;q=0.5', # 'Host' => 'www.mapcoordinates.net:443', 'Referer' => 'https://www.mapcoordinates.net/en', 'User-Agent' => 'Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Firef +ox/78.0', 'Content-Length' => $payloadlen, 'Content-Type' => 'application/x-www-form-urlencoded; charse +t=UTF-8', 'DNT' => '1', 'Origin' => 'https://www.mapcoordinates.net', 'X-Requested-With' => 'XMLHttpRequest' ], $payload ); die "call to HTTP::Request has failed" unless $req; if ( $debug > 0 ) { $logger->debug( "$0 : $payload\n$0 : sending above payload, of $payloadlen bytes +..."); } $response = $ua->request($req); die "Error fetching: " . $response->status_line unless $response->is_success; my $content = $response->decoded_content; my $data = Data::Roundtrip::json2perl($content); die "failed to parse received data:\n$content\n" unless exists $data->{'elevation'}; return $data->{'elevation'}; } sub distance { my ( $lat1, $lon1, $lat2, $lon2, $unit ) = @_; if ( ( $lat1 == $lat2 ) && ( $lon1 == $lon2 ) ) { return 0; } else { my $theta = $lon1 - $lon2; my $dist = sin( deg2rad($lat1) ) * sin( deg2rad($lat2) ) + cos( deg2rad($lat1) ) * cos( deg2rad($lat2) ) * cos( deg2rad($th +eta) ); $dist = acos($dist); $dist = rad2deg($dist); $dist = $dist * 60 * 1.1515; if ( $unit eq "K" ) { $dist = $dist * 1.609344; } elsif ( $unit eq "N" ) { $dist = $dist * 0.8684; } return ($dist); } } #::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +: #::: This function get the arccos function using arctan function :: +: #::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +: sub acos { my ($rad) = @_; my $ret = atan2( sqrt( 1 - $rad**2 ), $rad ); return $ret; } #::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +: #::: This function converts decimal degrees to radians :: +: #::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +: sub deg2rad { my ($deg) = @_; return ( $deg * $pi / 180 ); } #::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +: #::: This function converts radians to decimal degrees :: +: #::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +: sub rad2deg { my ($rad) = @_; return ( $rad * 180 / $pi ); } __END__

Cheers,

Replies are listed 'Best First'.
Re^3: approximating geological problems with highway data
by AnomalousMonk (Archbishop) on Jan 26, 2023 at 09:51 UTC
    In sub init_event { ... }
    { "name" => "snowville", "date" => $date_str, "time" => $time_str, "location" => { lon => -112.7105234, lat => 41.9655701 }, 42.152429, -112.9842191 },

    Is the 42.152429, -112.9842191 key/value pair from the posted code perhaps a tyop? It's syntactically correct, but looks semantically fishy. Looks like it escaped from the following event record anonymous hash. (Update: OTOH, it looks quite harmless since you're unlikely to ever encounter a "42.152429" key in your code. :)

    Also, if init_event() isn't just stub code for the SSCCE, a more succinct way to write it would be

    c:\@Work\Perl\monks>perl use strict; use warnings; use Data::Dump qw(dd); sub init_event { # define unique event records as anonymous hash refs. my $ar_events = [ { "name" => "Boise", "location" => { lon => -116.2, lat => 43.61 }, }, { "name" => "near sublett", "location" => { lon => -113.2104084, lat => 42.3278114 }, }, { "name" => "snowville", "location" => { lon => -112.7105234, lat => 41.9655701 }, }, { "name" => "juniper", "location" => { lon => -112.9842191, lat => 42.152429 }, }, # and so on... ]; # add standard date/time to each event record. my $date_str = "2021-10-14"; my $time_str = "03:22:31"; @{$_}{ qw(date time) } = ($date_str, $time_str) for @$ar_events; return $ar_events; } my $ar_ev = init_event(); dd $ar_ev; ^Z [ { date => "2021-10-14", location => { lat => "43.61", lon => "-116.2" }, name => "Boise", "time" => "03:22:31", }, { date => "2021-10-14", location => { lat => "42.3278114", lon => "-113.2104084" }, name => "near sublett", "time" => "03:22:31", }, { date => "2021-10-14", location => { lat => "41.9655701", lon => "-112.7105234" }, name => "snowville", "time" => "03:22:31", }, { date => "2021-10-14", location => { lat => "42.152429", lon => "-112.9842191" }, name => "juniper", "time" => "03:22:31", }, ]


    Give a man a fish:  <%-{-{-{-<

      Thanks all for helpful source comments. I can now compare numbers from different sources. This link should contain the newest source: 4.am.pl

      Output:

      $ ./4.am.pl Subroutine get_logger redefined at ./4.am.pl line 146. INFO: ./4.am.pl INFO: pi is 3.14159265358979 INFO: Bonneville max altitude in feet: 5200 INFO: Boise 43.61 -116.2 INFO: return from the google is 821 meters INFO: Altitude in feet is 2693.56964 INFO: Difference from max Bonneville elevation is 2506.43036 ft INFO: USGS elevation is 2697.37 ft INFO: Percent difference is 0.140990634426756 INFO: distance is 7794.07491881811 miles INFO: distance is 4656.32531168763 miles INFO: Percent difference is 50.4039958401085 INFO: ============== INFO: near sublett 42.3278114 -113.2104084 INFO: return from the google is 1455 meters INFO: Altitude in feet is 4773.6222 INFO: Difference from max Bonneville elevation is 426.3778 ft INFO: USGS elevation is 4780.72 ft INFO: Percent difference is 0.148577470880213 INFO: distance is 175.16979925839 miles INFO: distance is 175.486511142723 miles INFO: Percent difference is 0.180639489402675 INFO: ============== INFO: snowville 41.9655701 -112.7105234 INFO: return from the google is 1384 meters INFO: Altitude in feet is 4540.68256 INFO: Difference from max Bonneville elevation is 659.31744 ft INFO: USGS elevation is 4545.24 ft INFO: Percent difference is 0.100318706656456 INFO: distance is 35.8058824322112 miles INFO: distance is 35.837188345951 miles INFO: Percent difference is 0.0873941147406608 INFO: ============== INFO: juniper 42.152429 -112.9842191 INFO: return from the google is 1577 meters INFO: Altitude in feet is 5173.88468 INFO: Difference from max Bonneville elevation is 26.1153199999999 ft INFO: USGS elevation is 5169.2 ft INFO: Percent difference is 0.0905857419703596 INFO: distance is 19.0729839145012 miles INFO: distance is 19.0916186594743 miles INFO: Percent difference is 0.097654599897474 INFO: ============== $

      The first distance is kludged with garbage values, so not too surprised to see 50% error there, but look at how close the values tended to be! I think I might have 3 sig figs. The modified script is approximately:

      ### Elevation with USGS use Geo::WebService::Elevation::USGS; my $eq = Geo::WebService::Elevation::USGS->new(); my $alt = $eq->elevation( $event->{location}->{lat}, $event->{location}->{lo +n} ) ->{Elevation}; $logger->info("USGS elevation is $alt ft"); ### compare values my $percent = percent_error( $feet, $alt ); $logger->info("Percent difference is $percent"); ### home cooked distance my $d = distance( $prev_lat, $prev_long, $event->{location}->{lat}, $event->{location}->{lon}, "M" ); $logger->info("distance is $d miles"); ### distance with GPS::Point use GPS::Point; my $gps = GPS::Point->new( lat => $prev_lat, lon => $prev_long ); my $dist = $gps->distance( $event->{location}->{lat}, $event->{location}->{lo +n} ); my $feet2 = 3.28084 * $dist; my $miles = $feet2 / 5280; #$logger->info("distance is $feet2 feet"); $logger->info("distance is $miles miles"); ### compare values my $percent2 = percent_error( $d, $miles ); $logger->info("Percent difference is $percent2");

      Really happy to work up a different source for elevations, and look, I ran into Tom Wyant's software again:

      Running Build test for WYANT/Geo-WebService-Elevation-USGS-0.120.tar.g +z t/basic.t .. ok t/elev.t ... # Accessing https://nationalmap.gov/epqs/pqs.php

      Cheers,

Re^3: approximating geological problems with highway data
by Anonymous Monk on Jan 29, 2023 at 06:51 UTC
    use Math::Trig qw[:pi deg2rad rad2deg];