简体   繁体   中英

Delphi free XML parser / Reader for GPX files

I am looking for a free easy to use XML parser / Reader for GPX files in Delphi and was wondering if anyone could recommend one or should i be using Delphi's own XML data binding / XML document?

thanks

Colin

NativeXml

This is a small-footprint native Delphi implementation to read and write XML documents. It provides a fully object-oriented approach to working with XML files, with clearly defined and Delphi-focused properties, events and methods.

You can use this code to read and write XML documents from/to files, streams or strings. The load routine generates events that can be used to display load progress on the fly.

NativeXML Website

You can use the Tool "XML mapper" of Delphi.
Here on my blog, you can find the article "Load an GPX file (XML) y acces to data" that explain how use this tool (XML Mapper). The sample create structure to load GPX Files.

在此输入图像描述

You can find other similar post, like this "Generate KML files routes; Tracks on Google Maps" that use this tools also to generate KML files.

The webpage is originaly in Spanish, but you can use Google translate (on right of page). Also you can download the source code of samples and see/test it.

Regards.

OmniXML

It is free ( MPL 1.1 ), reasonably fast and implements the DOM Level 1 spec plus some MS extensions.

There are some OmniXML examples on the StackOverflow.

Alternate way with TNativeXML .

You can obviously derive specialized class from TNativeXml but in my code I used a helper class :

unit uHelper;

interface

uses
  SysUtils,
  Classes,
  NativeXml;

const
  // Do not localize these
  sNamespace  = 'http://www.topografix.com/GPX/1/1';
  sVersion = '1.1';
  sCreator = 'SwathGen';
  sSchemaInstance = 'http://www.w3.org/2001/XMLSchema-instance';
  sSchemaLocation = sNamespace+' '+
                    'http://www.topografix.com/GPX/1/1/gpx.xsd';

resourcestring
  sNoGpxRoot       = '<%s> has no gpx root !';
  sWrongGpxXmlns   = '<%s> root has a wrong xmlns attribute';
  sWrongGpxVersion = '<%s> is not a version "1.1" gpx file !';

type
  EGpxException = class(Exception)

  end;

type
  TGpxHelper = class helper for TNativeXml
  private
    function GetMetadataNode: TXmlNode;
    function GetWaypointNodes: TsdNodeList;
    function GetRouteNodes: TsdNodeList;
    function GetTrackNodes: TsdNodeList;
    function GetExtensionsNode: TXmlNode;
  public
    constructor CreateGpx(AOwner: TComponent);

    procedure NewGpx;

    procedure NodesAdd(ANodes: array of TXmlNode); overload;

    procedure AssignToStrings(AStrings:TStrings);

    function Element(const AName: string): TXmlNode;

    // File IO
    procedure LoadGpxFromFile(const AFileName: string);
    procedure SaveGpxToFile(const AFileName: string);

    property Metadata:TXmlNode read GetMetadataNode;
    property Waypoints:TsdNodeList read GetWaypointNodes;
    property Routes:TsdNodeList read GetRouteNodes;
    property Tracks:TsdNodeList read GetTrackNodes;
    property Extensions:TXmlNode read GetExtensionsNode;
  end;

  TGpxNodeHelper = class helper for TXmlNode
    function Element(const AName: string): TXmlNode;
  end;

implementation

{ TGpxHelper }

procedure TGpxHelper.AssignToStrings(AStrings:TStrings);
var
  lXmlFormat: TXmlFormatType;
  lIndentString: string;
begin
  // Save states
  lXmlFormat := XmlFormat;
  lIndentString := IndentString;

  XmlFormat := xfReadable;
  IndentString := '  ';

  AStrings.Text := WriteToString;

  // Restore states
  XmlFormat := lXmlFormat;
  IndentString := lIndentString;
end;

constructor TGpxHelper.CreateGpx(AOwner: TComponent);
begin
  inherited Create(AOwner);
  //
  NewGpx;
end;

function TGpxHelper.Element(const AName: string): TXmlNode;
begin
  Result := Root.Element(AName);
end;

function TGpxHelper.GetExtensionsNode: TXmlNode;
begin
  Result := Element('extensions');
end;

function TGpxHelper.GetMetadataNode: TXmlNode;
begin
  Result := Element('metadata');
end;

function TGpxHelper.GetRouteNodes: TsdNodeList;
begin
  Result := TsdNodeList.Create(False);

  Root.NodesByName('rte', Result);
end;

function TGpxHelper.GetTrackNodes: TsdNodeList;
begin
  Result := TsdNodeList.Create(False);

  Root.NodesByName('trk', Result);
end;

function TGpxHelper.GetWaypointNodes: TsdNodeList;
begin
  Result := TsdNodeList.Create(False);

  Root.NodesByName('wpt', Result);
end;

procedure TGpxHelper.LoadGpxFromFile(const AFileName: string);
var
  lGpx: TNativeXml;
  lFileName: TFileName;
begin
  lFileName := ExtractFileName(AFileName);

  lGpx := TNativeXml.Create(Self);

  lGpx.LoadFromFile(AFileName);

  try
    if lGpx.Root.Name<>'gpx' then
      raise EGpxException.CreateFmt(sNoGpxRoot,[lFileName])
    else if lGpx.Root.AttributeValue[Root.AttributeIndexByName('xmlns')]<>sNamespace then
      raise EGpxException.CreateFmt(sWrongGpxXmlns,[lFileName])
    else if lGpx.Root.AttributeValue[Root.AttributeIndexByName('version')]<>sVersion then
      raise EGpxException.CreateFmt(sWrongGpxVersion,[lFileName])
    else
      Self.ReadFromString(lGpx.WriteToString) // <<<
  finally
    lGpx.Free
  end;
end;

procedure TGpxHelper.NewGpx;
begin
  New;

  Root.Name := 'gpx';

  NodesAdd(
    [
      AttrText('xmlns', sNamespace),
      AttrText('version', sVersion),
      AttrText('creator', sCreator),
      AttrText('xmlns:xsi',sSchemaInstance),
      AttrText('xsi:schemaLocation', sSchemaLocation),

      // Metadata
      NodeNew('metadata',
        [
          NodeNewAttr('bounds',
            [
              AttrText('minlat','90.00000000'),
              AttrText('minlon','180.00000000'),
              AttrText('maxlat','-90.00000000'),
              AttrText('maxlon','-180.00000000')
            ]
          ),
          NodeNew('extensions')
        ]
      ),

      // Waypoints

      // Routes

      // Tracks

      NodeNew('extensions')
    ]
  );
end;

procedure TGpxHelper.NodesAdd(ANodes: array of TXmlNode);
begin
  Root.NodesAdd(ANodes);
end;

procedure TGpxHelper.SaveGpxToFile(const AFileName: string);
begin
  ChangeFileExt(AFileName,'gpx');

  SaveToFile(AFileName);  
end;

{ TGpxNodeHelper }

function TGpxNodeHelper.Element(const AName: string): TXmlNode;
begin
  Result := NodeByName(UTF8String(AName));
end;

end.

A practical sample using the uHelper unit :

unit ufrmMain;

interface

uses
  NativeXml,
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, SynEdit, SynMemo, SynEditHighlighter, SynHighlighterXML,
  SynEditMiscClasses, SynEditSearch, ComCtrls;

type
  TMainForm = class(TForm)
    BtnNew: TButton;
    BtnLoad: TButton;
    OpenDialog: TOpenDialog;
    BtnSave: TButton;
    SaveDialog: TSaveDialog;
    Memo: TSynMemo;
    XMLSyn: TSynXMLSyn;
    Search: TSynEditSearch;
    StatusBar: TStatusBar;
    procedure FormCreate(Sender: TObject);
    procedure BtnLoadClick(Sender: TObject);
    procedure FormShow(Sender: TObject);
    procedure BtnSaveClick(Sender: TObject);
    procedure BtnNewClick(Sender: TObject);
  private
    FGpx: TNativeXml;

    procedure ShowGpxInfo;
    procedure UpdateControls;
  public
    { Public declarations }
  end;

var
  MainForm: TMainForm;

implementation

uses
  Math,
  uHelper;

{$R *.dfm}

procedure TMainForm.BtnLoadClick(Sender: TObject);
begin
  OpenDialog.FileName := '';

  if OpenDialog.Execute then
  begin
    FGpx.LoadGpxFromFile(OpenDialog.FileName);

    UpdateControls;
    ShowGpxInfo
  end;
end;

procedure TMainForm.BtnNewClick(Sender: TObject);
begin
  FGpx.NewGpx;

  UpdateControls;
end;

procedure TMainForm.BtnSaveClick(Sender: TObject);
begin
  SaveDialog.FileName := '';

  if SaveDialog.Execute then
  begin
    FGpx.SaveGpxToFile(SaveDialog.FileName);
  end;
end;

procedure TMainForm.FormCreate(Sender: TObject);
begin
  FGpx := TNativeXml.CreateGpx(Self);
end;

procedure TMainForm.FormShow(Sender: TObject);
begin
  UpdateControls;
end;

procedure TMainForm.ShowGpxInfo;
const
  cLF = #10#13;
  cMsg  = 'metadata node count : %u'+cLF+
          'wpt node count : %u'+cLF+
          'rte node count : %u'+cLF+
          'trk node count : %u'+cLF+
          'extensions node count : %u';
var
  lMetadataCount: Integer;
  lWaypointsCount: Integer;
  lRoutesCount: Integer;
  lTracksCount: Integer;
  lExtensions: Integer;
begin
  lMetadataCount := IfThen(Assigned(FGpx.Metadata),1,0); 

  with FGpx.Waypoints do
  try
    lWaypointsCount := Count;
  finally
    Free
  end;

  with FGpx.Routes do
  try
    lRoutesCount := Count;
  finally
    Free
  end;

  with FGpx.Tracks do
  try
    lTracksCount := Count;
  finally
    Free
  end;

  lExtensions := IfThen(Assigned(FGpx.Extensions),1,0); 

  ShowMessage(Format(cMsg,[lMetadataCount,lWaypointsCount,lRoutesCount,lTracksCount,lExtensions]))
end;

procedure TMainForm.UpdateControls;
begin
  FGpx.AssignToStrings(Memo.Lines); // <<<
end;

end.

You can use SimpleStorage .

Sample code accessing Gpx file :

interface

uses
  ...
  Cromis.SimpleStorage, ...

type
  TMainForm = class(TForm)
    ...
    MmGpx: TMemo;
    BtnLoad: TButton;
    OpenDialog: TOpenDialog;
    ...
  private
    FGpxStorage: ISimpleStorage;
  protected
    procedure ShowGpx;
  end;

...

implementation

procedure TMainForm.BtnLoadClick(Sender: TObject);
begin
  if OpenDialog.Execute then
    FGpxStorage := StorageFromFile(OpenDialog.FileName); // <<<
    ShowGpx;
  end;
end;

procedure TMainForm.ShowGpx;
begin
  MmGpx.Lines.Text := FGpxStorage.Content(True); // <<<
end;

Moreover, you can still find here a template of using SimpleStorage to generate compliant new Gpx file from scratch.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM