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,
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: <%-{-{-{-<
| [reply] [d/l] [select] |
|
$ ./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, | [reply] [d/l] [select] |
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];
| [reply] [d/l] |
|
| [reply] [d/l] [select] |
|
|