Re: Parsing GPX with SimpleXML

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



I stepped away from the computer for a bit, but I wanted to respond to your email.

Yes I could modify the file, but I have hundreds of them to process. The Geo Tracker app simply uploads a new file to the server every time and I want to run a cron task to process, without any intervention on my part. And while I can code the modification in the script, that's just adding another step in the process.

As for the warnings you're getting... Odd as I don't get any on my system. Although that could very well be because I may have them turned off. I need to check.

I'll try your proposed option #2 in a bit and report back. 

A

On Wed, Dec 18, 2019, 1:45 PM Stefan A. <acid24@xxxxxxxxx> wrote:
Try these two options as well. That element is problematic because it depicts a namespace in XML, so I added the namespace declaration to the GPX document like this:

<gpx version="1.1" xmlns:geotracker="http://my.foo.url">
  <trk>
    <trkseg>
      <trkpt lat="39.998454" lon="-105.086447">
              <ele>1568</ele>
              <time>2019-12-16T10:59:47.000Z</time>
              <extensions>
                <geotracker:meta s="0" />
              </extensions>
            </trkpt>
            <trkpt lat="39.998553" lon="-105.086417">
              <ele>1577</ele>
              <time>2019-12-16T11:01:39.000Z</time>
              <extensions>
                <geotracker:meta c="0.03" s="1.83" />
              </extensions>
            </trkpt>
            <trkpt lat="39.998654" lon="-105.086395">
              <ele>1572</ele>
              <time>2019-12-16T11:01:44.000Z</time>
              <extensions>
                <geotracker:meta c="0.3" s="2.08" />
              </extensions>
            </trkpt>
    </trkseg>
  </trk>
</gpx>

Then in the PHP code I did this
<?php

$gpx = simplexml_load_file('test.gpx', SimpleXMLElement::class, LIBXML_NOBLANKS | LIBXML_PEDANTIC);

foreach ($gpx->trk as $trk) {
    foreach ($trk->trkseg as $seg) {
        foreach ($seg->trkpt as $pt) {
            $attrs = $pt->extensions->children('http://my.foo.url')[0]->attributes();
            echo "lat: ", $pt["lat"], "   lon: ", $pt["lon"];
            echo "    ele: ", $pt->ele, "   time: ", $pt->time;
            echo "    extensions: c: ", $attrs['c'] ?? 'N/A';
            echo "    s: ", $attrs['s'] ?? 'N/A', "\n";
        }
    }
}

which yields the expected output

lat: 39.998454   lon: -105.086447    ele: 1568   time: 2019-12-16T10:59:47.000Z    extensions: c: N/A    s: 0
lat: 39.998553   lon: -105.086417    ele: 1577   time: 2019-12-16T11:01:39.000Z    extensions: c: 0.03    s: 1.83
lat: 39.998654   lon: -105.086395    ele: 1572   time: 2019-12-16T11:01:44.000Z    extensions: c: 0.3    s: 2.08

However I don't know if you're able to update the gpx document so another option I tried (with the original gpx content you posted):
<?php

$gpx = simplexml_load_file('test.gpx', SimpleXMLElement::class, LIBXML_NOBLANKS | LIBXML_PEDANTIC);

foreach ($gpx->trk as $trk) {
    foreach ($trk->trkseg as $seg) {
        foreach ($seg->trkpt as $pt) {
            $attrs = $pt->extensions->children()[0]->attributes();
            echo "lat: ", $pt["lat"], "   lon: ", $pt["lon"];
            echo "    ele: ", $pt->ele, "   time: ", $pt->time;
            echo "    extensions: c: ", $attrs['c'] ?? 'N/A';
            echo "    s: ", $attrs['s'] ?? 'N/A', "\n";
        }
    }
}

which outputs a lot of warnings like: PHP Warning:  simplexml_load_file(): test.gpx:8: namespace error : Namespace prefix geotracker on meta is not defined, but also yields the expected output

lat: 39.998454   lon: -105.086447    ele: 1568   time: 2019-12-16T10:59:47.000Z    extensions: c: N/A    s: 0
lat: 39.998553   lon: -105.086417    ele: 1577   time: 2019-12-16T11:01:39.000Z    extensions: c: 0.03    s: 1.83
lat: 39.998654   lon: -105.086395    ele: 1572   time: 2019-12-16T11:01:44.000Z    extensions: c: 0.3    s: 2.08

Hope this helps

On Wed, Dec 18, 2019 at 7:04 PM Ashley M. Kirchner <kirash4@xxxxxxxxx> wrote:
Running from CLI. Here's everything:

[2] 10:59:18 <kirash@athenaeum:~/GPX> ls -al
total 7452
drwxrwxr-x   2 kirash kirash    4096 Dec 18 10:59 ./
drwx------. 11 kirash kirash    4096 Dec 18 10:59 ../
-rw-rw-r--   1 kirash kirash 7616128 Dec 18 10:37 Dec_16,_2019_3_59_47_AM.gpx
-rw-rw-r--   1 kirash kirash     735 Dec 18 10:36 parseGPX.php


[3] 10:59:23 <kirash@athenaeum:~/GPX> cat parseGPX.php  
<?php
//open gpx file
$gpx = simplexml_load_file("Dec_16,_2019_3_59_47_AM.gpx", SimpleXMLElement::class, LIBXML_NOBLANKS);
$count = 0;
foreach ($gpx->trk as $trk) {
    foreach ($trk->trkseg as $seg) {
        foreach ($seg->trkpt as $pt) {
            echo "lat: ", $pt["lat"], "   lon: ", $pt["lon"];
            echo "    ele: ", $pt->ele, "   time: ", $pt->time, "\n";
            var_dump($pt->extensions);
            //echo "    extensions: c: ", $pt->extensions->{'geotracker:meta'}['c'] ?? 'N/A';
            //echo "    s: ", $pt->extensions->{'geotracker:meta'}['s'] ?? 'N/A', "\n";

            // I don't want to spam the full file, it's large. Just want to see it work first.
            $count++;
            if ($count > 2) {
                exit();
            }
        }
    }
}
unset($gpx);
?>

[4] 10:59:25 <kirash@athenaeum:~/GPX> php --version
PHP 7.4.1 (cli) (built: Dec 17 2019 16:35:58) ( NTS )
Copyright (c) The PHP Group
Zend Engine v3.4.0, Copyright (c) Zend Technologies


[5] 10:59:31 <kirash@athenaeum:~/GPX> php ./parseGPX.php
lat: 39.998454   lon: -105.086447    ele: 1568   time: 2019-12-16T10:59:47.000Z
object(SimpleXMLElement)#11 (0) {
}
lat: 39.998553   lon: -105.086417    ele: 1577   time: 2019-12-16T11:01:39.000Z
object(SimpleXMLElement)#10 (0) {
}
lat: 39.998654   lon: -105.086395    ele: 1572   time: 2019-12-16T11:01:44.000Z
object(SimpleXMLElement)#11 (0) {
}

[6] 10:59:36 <kirash@athenaeum:~/GPX> 


Nothing particularly fancy.

On Wed, Dec 18, 2019 at 10:55 AM Stefan A. <acid24@xxxxxxxxx> wrote:
How are you running this code? CLI? Webserver? Do you have any errors/warnings/notices in the output/logs? Have you considered parsing that file with the DOM extension?

On Wed, Dec 18, 2019 at 6:42 PM Ashley M. Kirchner <kirash4@xxxxxxxxx> wrote:
Sorry, yeah I posted a slightly modified version of the gpx file because I've been trying to figure out what works and what doesn't. The second record in the posted snippet was missing the 'meta' namespace. Here's the correct (or rather, original) one:

      <trkpt lat="39.998454" lon="-105.086447">
        <ele>1568</ele>
        <time>2019-12-16T10:59:47.000Z</time>
        <extensions>
          <geotracker:meta s="0" />
        </extensions>
      </trkpt>
      <trkpt lat="39.998553" lon="-105.086417">
        <ele>1577</ele>
        <time>2019-12-16T11:01:39.000Z</time>
        <extensions>
          <geotracker:meta c="0.03" s="1.83" />
        </extensions>
      </trkpt>
      <trkpt lat="39.998654" lon="-105.086395">
        <ele>1572</ele>
        <time>2019-12-16T11:01:44.000Z</time>
        <extensions>
          <geotracker:meta c="0.3" s="2.08" />
        </extensions>
      </trkpt>



If I were to manually remove the ':meta' part, then I can get the 'c' and 's' attributes just fine. But with it in its original form, nothing. Adding a var_dump just gave me bupkis ...

lat: 39.998454   lon: -105.086447    ele: 1568   time: 2019-12-16T10:59:47.000Z
object(SimpleXMLElement)#11 (0) {
}
lat: 39.998553   lon: -105.086417    ele: 1577   time: 2019-12-16T11:01:39.000Z
object(SimpleXMLElement)#10 (0) {
}
lat: 39.998654   lon: -105.086395    ele: 1572   time: 2019-12-16T11:01:44.000Z
object(SimpleXMLElement)#11 (0) {
}

On Wed, Dec 18, 2019 at 10:23 AM Stefan A. <acid24@xxxxxxxxx> wrote:
Try adding some var_dumps in the innermost foreach (var_dump($pt->extensions);) maybe that will give some clues about what the issue is.

On Wed, Dec 18, 2019 at 6:18 PM Stefan A. <acid24@xxxxxxxxx> wrote:
With the contents of what you posted in the previous email, and with this code (added suppressing of errors because I got a bunch of warnings about geotracker:meta not being defined as a namespace) 

<?php

$gpx = simplexml_load_file('test.gpx', SimpleXMLElement::class, LIBXML_NOBLANKS | LIBXML_NOERROR);

foreach ($gpx->trk as $trk) {
    foreach ($trk->trkseg as $seg) {
        foreach ($seg->trkpt as $pt) {
            echo "lat: ", $pt["lat"], "   lon: ", $pt["lon"];
            echo "    ele: ", $pt->ele, "   time: ", $pt->time;
            echo "    extensions: c: ", $pt->extensions->{'geotracker:meta'}['c'] ?? 'N/A';
            echo "    s: ", $pt->extensions->{'geotracker:meta'}['s'] ?? 'N/A', "\n";
        }
    }
}

I get

lat: 39.998454   lon: -105.086447    ele: 1568   time: 2019-12-16T10:59:47.000Z    extensions: c: N/A    s: 0
lat: 39.998553   lon: -105.086417    ele: 1577   time: 2019-12-16T11:01:39.000Z    extensions: c: N/A    s: N/A
lat: 39.998654   lon: -105.086395    ele: 1572   time: 2019-12-16T11:01:44.000Z    extensions: c: 0.3    s: 2.08

So I don't understand what the problem is either.

On Wed, Dec 18, 2019 at 5:59 PM Ashley M. Kirchner <kirash4@xxxxxxxxx> wrote:
The only thing that stands out is the minor version difference, you have 2.9.4 whereas I have 2.9.1 (and on CentOS there is no update to 2.9.4.)

xmlrpc_error_number => 0 => 0
xmlrpc_errors => Off => Off
libxml Version => 2.9.1
libxml
libXML support => active
libXML Compiled Version => 2.9.1
libXML Loaded Version => 20901
libXML streams => enabled
mbstring.http_output_conv_mimetypes => ^(text/|application/xhtml\+xml) => ^(text/|application/xhtml\+xml)
SimpleXML
SimpleXML support => enabled
xml
XML Support => active
XML Namespace Support => active
libxml2 Version => 2.9.1
xmlreader
XMLReader => enabled
xmlwriter
XMLWriter => enabled
libxslt compiled against libxml Version => 2.9.1

So here's the full code now:

<?php
//open gpx file
$gpx = simplexml_load_file("Dec_16,_2019_3_59_47_AM.gpx", SimpleXMLElement::class, LIBXML_NOBLANKS);
foreach ($gpx->trk as $trk) {
    foreach ($trk->trkseg as $seg) {
        foreach ($seg->trkpt as $pt) {
            echo "lat: ", $pt["lat"], "   lon: ", $pt["lon"];
            echo "    ele: ", $pt->ele, "   time: ", $pt->time, "\n";
            echo "    extensions: c: ", $pt->extensions->{'geotracker:meta'}['c'] ?? 'N/A';
            echo "    s: ", $pt->extensions->{'geotracker:meta'}['s'] ?? 'N/A', "\n";
        }
    }
}
unset($gpx);
?>

... and this is what I get:

lat: 39.998454   lon: -105.086447    ele: 1568   time: 2019-12-16T10:59:47.000Z
    extensions: c: N/A    s: N/A
lat: 39.998553   lon: -105.086417    ele: 1577   time: 2019-12-16T11:01:39.000Z
    extensions: c: N/A    s: N/A
lat: 39.998654   lon: -105.086395    ele: 1572   time: 2019-12-16T11:01:44.000Z
    extensions: c: N/A    s: N/A

Those three records are:

    <trkseg>
      <trkpt lat="39.998454" lon="-105.086447">
        <ele>1568</ele>
        <time>2019-12-16T10:59:47.000Z</time>
        <extensions>
          <geotracker:meta s="0" />
        </extensions>
      </trkpt>
      <trkpt lat="39.998553" lon="-105.086417">
        <ele>1577</ele>
        <time>2019-12-16T11:01:39.000Z</time>
        <extensions>
          <geotracker c="0.03" s="1.83" />
        </extensions>
      </trkpt>
      <trkpt lat="39.998654" lon="-105.086395">
        <ele>1572</ele>
        <time>2019-12-16T11:01:44.000Z</time>
        <extensions>
          <geotracker:meta c="0.3" s="2.08" />
        </extensions>
      </trkpt>


I'm clearly missing something here but I can't figure out what ... By the way, thank you for your help so far. Much appreciated!

A



On Wed, Dec 18, 2019 at 8:41 AM Stefan A. <acid24@xxxxxxxxx> wrote:
This is the output of the command php -i | grep xml

$ php -i | grep xml
Configure Command =>  './configure'  '--prefix=/usr/local/Cellar/php@7.2/7.2.23' '--localstatedir=/usr/local/var' '--sysconfdir=/usr/local/etc/php/7.2' '--with-config-file-path=/usr/local/etc/php/7.2' '--with-config-file-scan-dir=/usr/local/etc/php/7.2/conf.d' '--with-pear=/usr/local/Cellar/php@7.2/7.2.23/share/php@7.2/pear' '--enable-bcmath' '--enable-calendar' '--enable-dba' '--enable-dtrace' '--enable-exif' '--enable-ftp' '--enable-fpm' '--enable-intl' '--enable-mbregex' '--enable-mbstring' '--enable-mysqlnd' '--enable-opcache-file' '--enable-pcntl' '--enable-phpdbg' '--enable-phpdbg-webhelper' '--enable-shmop' '--enable-soap' '--enable-sockets' '--enable-sysvmsg' '--enable-sysvsem' '--enable-sysvshm' '--enable-wddx' '--enable-zip' '--with-apxs2=/usr/local/opt/httpd/bin/apxs' '--with-bz2=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk/usr' '--with-curl=/usr/local/opt/curl-openssl' '--with-fpm-user=_www' '--with-fpm-group=_www' '--with-freetype-dir=/usr/local/opt/freetype' '--with-gd' '--with-gettext=/usr/local/opt/gettext' '--with-gmp=/usr/local/opt/gmp' '--with-iconv=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk/usr' '--with-icu-dir=/usr/local/opt/icu4c' '--with-jpeg-dir=/usr/local/opt/jpeg' '--with-kerberos=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk/usr' '--with-layout=GNU' '--with-ldap=/usr/local/opt/openldap' '--with-ldap-sasl=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk/usr' '--with-libxml-dir=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk/usr' '--with-libedit=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk/usr' '--with-libzip' '--with-mhash=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk/usr' '--with-mysql-sock=/tmp/mysql.sock' '--with-mysqli=mysqlnd' '--with-ndbm=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk/usr' '--with-openssl=/usr/local/opt/openssl@1.1' '--with-password-argon2=/usr/local/opt/argon2' '--with-pdo-dblib=/usr/local/opt/freetds' '--with-pdo-mysql=mysqlnd' '--with-pdo-odbc=unixODBC,/usr/local/opt/unixodbc' '--with-pdo-pgsql=/usr/local/opt/libpq' '--with-pdo-sqlite=/usr/local/opt/sqlite' '--with-pgsql=/usr/local/opt/libpq' '--with-pic' '--with-png-dir=/usr/local/opt/libpng' '--with-pspell=/usr/local/opt/aspell' '--with-sodium=/usr/local/opt/libsodium' '--with-sqlite3=/usr/local/opt/sqlite' '--with-tidy=/usr/local/opt/tidy-html5' '--with-unixODBC=/usr/local/opt/unixodbc' '--with-webp-dir=/usr/local/opt/webp' '--with-xmlrpc' '--with-xsl=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk/usr' '--with-zlib=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk/usr'
xmlrpc_error_number => 0 => 0
xmlrpc_errors => Off => Off
libxml Version => 2.9.4
libxml
mbstring.http_output_conv_mimetypes => ^(text/|application/xhtml\+xml) => ^(text/|application/xhtml\+xml)
Simplexml support => enabled
xml
libxml2 Version => 2.9.4
xmlreader
xmlrpc
core library version => xmlrpc-epi v. 0.51
homepage => http://xmlrpc-epi.sourceforge.net
xmlwriter
libxslt compiled against libxml Version => 2.9.4

On Wed, Dec 18, 2019 at 3:08 PM Ashley M. Kirchner <kirash4@xxxxxxxxx> wrote:
Yeah I tried that. Didn't work. May I ask what XML extension you're using?

On Wed, Dec 18, 2019 at 5:07 AM Stefan A. <acid24@xxxxxxxxx> wrote:
This worked for me on PHP 7.2

foreach ($gpx->trk as $trk) {
    foreach ($trk->trkseg as $seg) {
        foreach ($seg->trkpt as $pt) {
            echo "lat: ", $pt["lat"], "   lon: ", $pt["lon"];
            echo "    ele: ", $pt->ele, "   time: ", $pt->time;
            echo "    extensions: c: ", $pt->extensions->{'geotracker:meta'}['c'] ?? 'N/A', " s: ", $pt->extensions->{'geotracker:meta'}['s'] ?? 'N/A', "\n";
        }
    }
}

On Wed, Dec 18, 2019 at 1:04 PM Aziz Saleh <azizsaleh@xxxxxxxxx> wrote:

On Tue, Dec 17, 2019 at 10:56 PM Ashley M. Kirchner <kirash4@xxxxxxxxx> wrote:
Hi folks,

I'm having a hard time figuring this one out. I have the following (snippet) of a GPX file:

<gpx version="1.1" ...>
  <trk>
    <trkseg>
      <trkpt lat="39.998454" lon="-105.086447">
        <ele>1568</ele>
        <time>2019-12-16T10:59:47.000Z</time>
        <extensions>
          <geotracker:meta s="0" />
        </extensions>
      </trkpt>
      <trkpt lat="39.998553" lon="-105.086417">
        <ele>1577</ele>
        <time>2019-12-16T11:01:39.000Z</time>
        <extensions>
          <geotracker:meta c="0.03" s="1.83" />
        </extensions>
      </trkpt>
      ...
      ...
    </trkseg>
  </trk>
</gpx>

Getting to the most of the nodes is easy:

<?php
//open gpx file
$gpx = simplexml_load_file("Dec_16,_2019_3_59_47_AM.gpx");

foreach ($gpx->trk as $trk) {
    foreach ($trk->trkseg as $seg) {
        foreach ($seg->trkpt as $pt) {
            echo "lat: ", $pt["lat"], "   lon: ", $pt["lon"];
            echo "    ele: ", $pt->ele, "   time: ", $pt->time, "\n";
        }
    }
}
unset($gpx);
?>

But for the life of me, I can't figure out how to get information that's on the geotracker:meta line. Also note that not every single one of them will have both the 'c' and 's' attributes. Does anyone have any pointers pls?

A

Converting it to array should make it easier for you:

 

[Index of Archives]     [PHP Home]     [Apache Users]     [PHP on Windows]     [Kernel Newbies]     [PHP Install]     [PHP Classes]     [Pear]     [Postgresql]     [Postgresql PHP]     [PHP on Windows]     [PHP Database Programming]     [PHP SOAP]

  Powered by Linux