hujunsimon has asked for the wisdom of the Perl Monks concerning the following question:

Hello Monks !

I have a question of regarding to calculate headings of several aircrafts movements from a series of GPS data, then i can use perl subroutine to convert the code into kml and display it on GoogleEarth, so the aircraft icon could rotate as time shifts.

i have already written some code to convert the code to kml, but not the headings, i know the method i should use to calculate the heading is something like following (very crude method), but i don't know how to implement into perl. it needs to use kind of array to store current point and way point, then computer the headings from two points.

I need helps, any clever Perlmonks out there could provide me some wisdom. below provide code i written so far and input data sample

Many thanks in advance !

Psuedo code for heading calculation

dlat = lat2 - lat1 dlon = lon2 - lon1 y = sin(lon2-lon1)*cos(lat2) x = cos(lat1)*sin(lat2)-sin(lat1)*cos(lat2)*cos(lon2-lon1) if y > 0 then if x > 0 then tc1 = arctan(y/x) if x < 0 then tc1 = 180 - arctan(-y/x) if x = 0 then tc1 = 90 if y < 0 then if x > 0 then tc1 = -arctan(-y/x) if x < 0 then tc1 = arctan(y/x)-180 if x = 0 then tc1 = 270 if y = 0 then if x > 0 then tc1 = 0 if x < 0 then tc1 = 180 if x = 0 then [the 2 points are the same]

perl code

#!/usr/bin/perl use POSIX qw(ceil floor); open(INPUT, $infile); @lines = <INPUT>; close(INPUT); open(OUTPUT, ">$outfile"); sub trim($); $timeCounter = 0; $seconds = 0; $minutes = 0; $hours = 0; $comma = ","; $space = " "; print OUTPUT "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"."\n"; print OUTPUT "<kml xmlns=\"http://earth.google.com/kml/2.1\">"."\n"; print OUTPUT "<Folder>"."\n"; foreach $currentLine(@lines) { @lineBuffer = split($delimiter,$currentLine); #--- At this point, we have the data from the current line. #--- Get all the elements $rowid = trim(@lineBuffer[0]); $aircraftid = trim(@lineBuffer[1]); $actual_date_time = trim(@lineBuffer[2]); #$end_time = trim(@lineBuffer[3]); $latitude = trim(@lineBuffer[3]); $longitude = trim(@lineBuffer[4]); $radio_altitude = trim(@lineBuffer[5]); $ground_speed = trim(@lineBuffer[6]); $thrust_engine_1 = trim(@lineBuffer[7]); $thrust_engine_2 = trim(@lineBuffer[8]); #--- Now, convert start and end times into UTC @timeString = split($space,$actual_date_time); $actual_date_time = @timeString[0]."T".@timeString[1]; #--- Determine the bearing $icon = "./blue_plane.png"; print OUTPUT "<Placemark><Style><IconStyle><scale>1.0</scale><head +ing>90</heading><Icon><href>$icon</href></Icon></IconStyle></Style><P +oint><coordinates>"; print OUTPUT $longitude.$comma.$latitude.$comma.$radio_altitude; print OUTPUT "</coordinates><altitudeMode>relativeToGround</altitu +deMode></Point>"; print OUTPUT "<TimeStamp><when>$actual_date_time</when></TimeStamp +>"; print OUTPUT "<description>"."ID:".$rowid."<hr/>"."time:".$actual_ +date_time."<hr/>"."Ground_speed:".$ground_speed."<hr/>"."Thrust_engin +e_1:".$thrust_engine_1."<hr/>"."thrust_engine_2:".$thrust_engine_2."< +hr/>"."nox_total:".$nox_total."<hr/>"."co_total:".$co_total."<hr/>"." +longitude:".$longitude."<hr/>"."latitude:".$latitude."<hr/>"."radio_a +ltitude:".$radio_altitude."<hr/></description>"; print OUTPUT "</Placemark>"."\n"; } print OUTPUT "</Folder></kml>";

Data sample of several aircraft GPS data:

"rowid","aircraft_id","actual_date_time","latitude","longitude","radio +_altitude","ground_speed","thrust_engine_1","thrust_engine_2" 3828994,599,2012-11-04 14:00:00,51.47592545,-0.437049866,-9.0,2.0,0.04 +4653355,0.041778761,1.74208527619370935228499282740898271690901934191 +164070390564,15.22092881857486030932218666006017736321211426212568671 +811012 3828995,591,2012-11-04 14:00:00,51.47598267,-0.435331702,-0.875,0.0,0. +066208174,0.068682498,0.913095793953437657822846430541224159086453865 +40627114668956,4.8940906898850944664550883969746212941234464464906348 +3492349 3828996,599,2012-11-04 14:00:01,51.47592545,-0.437049866,-9.0,2.0,0.04 +4653355,0.041778761,1.74208527619370935228499282740898271690901934191 +164070390564,15.22092881857486030932218666006017736321211426212568671 +811012 3828997,591,2012-11-04 14:00:01,51.47598267,-0.435331702,-0.75,0.0,0.0 +64970428,0.068682498,0.9055060385715144395793861983988312690635603870 +3789731965392,4.92096836684002218971971504299700119099094370033875757 +916224 3828998,599,2012-11-04 14:00:02,51.47592545,-0.437049866,-9.0,2.0,0.04 +4653355,0.041778761,1.74208527619370935228499282740898271690901934191 +164070390564,15.22092881857486030932218666006017736321211426212568671 +811012 3828999,591,2012-11-04 14:00:02,51.47598267,-0.435331702,-0.625,0.0,0. +066208174,0.068682498,0.913095793953437657822846430541224159086453865 +40627114668956,4.8940906898850944664550883969746212941234464464906348 +3492349 3829000,599,2012-11-04 14:00:03,51.47592545,-0.437049866,-9.0,2.0,0.04 +4653355,0.041

Replies are listed 'Best First'.
Re: Calculate aircraft headings from GPS data for google kml display
by BrowserUk (Patriarch) on Jun 09, 2014 at 18:28 UTC

    You almost certainly don't need this logic:

    if y > 0 then if x > 0 then tc1 = arctan(y/x) if x < 0 then tc1 = 180 - arctan(-y/x) if x = 0 then tc1 = 90 if y < 0 then if x > 0 then tc1 = -arctan(-y/x) if x < 0 then tc1 = arctan(y/x)-180 if x = 0 then tc1 = 270 if y = 0 then if x > 0 then tc1 = 0 if x < 0 then tc1 = 180 if x = 0 then [the 2 points are the same]

    Just feed y and x to atan2 and it will take care of the quadrant logic.

    Eg. This prints the angle in radians for the four cardinal directions followed by those for the four intercardinal directions:

    print atan2( $_->[0], $_->[1] ) for [ 0,-1],[ 0,+1],[-1, 0],[+1, 0], [+1,+1],[-1,+1],[-1,-1],[+1,-1];; 3.14159265358979 0 -1.5707963267949 1.5707963267949 0.785398163397448 -0.785398163397448 -2.35619449019234 2.35619449019234

    With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.

      Thank you for all the comments. But my main problem is still how to loop around all these big dataset and calculate the headings. i think the logic should be something like this:

      for ($aircraft_id){ $current_point_x = $latitude $current_point_y = $longitude $waypoint_x = $latitude_next $waypoint_y = $longitude_next //do some calculations on calculate headings ! //output headings }

      Any ideas to parse through all these data and do calculations, the data is organised in a way that there are different aircraft in the dataset, but it can be identified by aircraft_id, and they are ordered by gps_time column, how could i get my two continuous points and calculate the heading. In Python, i could use dictionary to do that, as to perl, i don't know. help please !

      thank you!

      Si

        In Python, i could use dictionary to do that

        In Perl, a "dictionary" is called a hash.

        As you have multiple aircraft, you cannot use the rowids to determine consecutive coordinates for a given aircraft (unless there are always and only 2), so I've used a hash of arrays of hashes to accumulate the data in:

        $planes{ $aid }[ n ]{ ... };

        This print the headings for each aircraft as it reads the data:

        C:\test>junk53 Aircraft: 599 heading: 0.000 Aircraft: 591 heading: 90.000 Aircraft: 599 heading: 0.000 Aircraft: 591 heading: 90.000

        Would've been nice if the sample contained some heading changes, and I'm somewhat suspicious of the exact 90° difference in the aircraft headings, so check the math. but this should get you started:

        #! perl -slw use strict; use Data::Dump qw[ pp ]; my @headers = split ' ', do{ my $raw = <DATA>; $raw =~ tr[",][ ]; $raw + }; #" my %planes; while( my $line = <DATA> ) { $line =~ tr[",][ ]; #" my @fields = split ' ', $line; my $rowid = shift @fields; my $aid = shift @fields; my %row; @row{ @headers } = @fields; push @{ $planes{ $aid } }, \%row; next unless @{ $planes{ $aid } } > 1; my $lat1 = $planes{ $aid }[ -2 ]{ latitude }; my $lat2 = $planes{ $aid }[ -1 ]{ latitude }; my $dlat = $lat2 - $lat1; my $lon1 = $planes{ $aid }[ -2 ]{ longitude }; my $lon2 = $planes{ $aid }[ -1 ]{ longitude }; my $dlon = $lon2 - $lon1; my $y = sin( $dlon / 180 ) * cos( $lat2 /180 ); my $x = cos( $lat1 /180 ) * sin( $lat2/180 ) - sin( $lat1/180 ) * +cos( $lat2/180 ) * cos( $dlon/180 ); printf "Aircraft: %d heading: %6.3f\n", $aid, atan2( $y, $x ) * 57 +.2957795; } __DATA__ "rowid","aircraft_id","actual_date_time","latitude","longitude","radio +_altitude","ground_speed","thrust_engine_1","thrust_engine_2" 3828994,599,2012-11-04 14:00:00,51.47592545,-0.437049866,-9.0,2.0,0.04 +4653355,0.041778761,1.74208527619370935228499282740898271690901934191 +164070390564,15.22092881857486030932218666006017736321211426212568671 +811012 3828995,591,2012-11-04 14:00:00,51.47598267,-0.435331702,-0.875,0.0,0. +066208174,0.068682498,0.913095793953437657822846430541224159086453865 +40627114668956,4.8940906898850944664550883969746212941234464464906348 +3492349 3828996,599,2012-11-04 14:00:01,51.47592545,-0.437049866,-9.0,2.0,0.04 +4653355,0.041778761,1.74208527619370935228499282740898271690901934191 +164070390564,15.22092881857486030932218666006017736321211426212568671 +811012 3828997,591,2012-11-04 14:00:01,51.47598267,-0.435331702,-0.75,0.0,0.0 +64970428,0.068682498,0.9055060385715144395793861983988312690635603870 +3789731965392,4.92096836684002218971971504299700119099094370033875757 +916224 3828998,599,2012-11-04 14:00:02,51.47592545,-0.437049866,-9.0,2.0,0.04 +4653355,0.041778761,1.74208527619370935228499282740898271690901934191 +164070390564,15.22092881857486030932218666006017736321211426212568671 +811012 3828999,591,2012-11-04 14:00:02,51.47598267,-0.435331702,-0.625,0.0,0. +066208174,0.068682498,0.913095793953437657822846430541224159086453865 +40627114668956,4.8940906898850944664550883969746212941234464464906348 +3492349

        With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
        Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
        "Science is about questioning the status quo. Questioning authority".
        In the absence of evidence, opinion is indistinguishable from prejudice.

        Can you prove a small sample of the dataset that illustrates the structure?

        DOH!

        You must always remember that the primary goal is to drain the swamp even when you are hip-deep in alligators.
Re: Calculate aircraft headings from GPS data for google kml display
by wjw (Priest) on Jun 09, 2014 at 18:05 UTC

    Have you taken a look at the GEO:: Namespace? Or Compass::Bearing Looks promising too. Might be something there that will save you a bit of time...

    Hope something there is helpful...

    ...the majority is always wrong, and always the last to know about it...

    Insanity: Doing the same thing over and over again and expecting different results...

    A solution is nothing more than a clearly stated problem...otherwise, the problem is not a problem, it is a facct

      Thank you wjw, i'm also looking into the package you suggested, :-). Will let you know if anything works, thanks !

Re: Calculate aircraft headings from GPS data for google kml display
by neilwatson (Priest) on Jun 09, 2014 at 17:59 UTC

      Thanks Neil, i actually don't have a problem of creating kml. the difficulty i'm having is to create the headings from these raw data. In python, i could use dictionary to achieve this, but for Perl, i don't know and i want to find a solution in perl, :-) !