unit dattokml;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, FileUtil, LResources, Forms, Controls, Graphics, Dialogs,
  ExtCtrls, StdCtrls,math;
type

  { TForm7 }
  TForm7 = class(TForm)
    SelectAndConvertButton: TButton;
    SchemeImage: TImage;
    StatusLine: TLabeledEdit;
    HelpNotes: TMemo;
    OpenLogDialog: TOpenDialog;
    ColorSchemeGroup: TRadioGroup;
    procedure SelectAndConvertButtonClick(Sender: TObject);
    procedure FormShow(Sender: TObject);
    procedure ColorSchemeGroupClick(Sender: TObject);
  private
    procedure DrawImage();
  public


  end;
type TKMLColors = array of String;
type TKMLValues = array of Double;

var
  Form7: TForm7;

  //Color display will be done on readings greater than color
  //  value according to new atlas <0.176 mcd/m^2 equates to >21.97 mpsas.
  //Cleardarkskys legend looks like it was
  //  expanded from The World Atlas of the Artificial Night Sky Brightness
  KMLColorsCleardarksky: TKMLColors;
  KMLValuesCleardarksky: TKMLValues;

  KMLColorsNewatlas: TKMLColors;
  KMLValuesNewatlas: TKMLValues;

  KMLColorsSelected: TKMLColors;
  KMLValuesSelected: TKMLValues;
implementation
uses appsettings, Unit1, header_utils, strutils
  , LazFileUtils; //Necessary for filename extraction

var

  SelectedTheme:String;

  LegendFilename:String;


procedure TForm7.FormShow(Sender: TObject);
begin
     //Get previous legend color theme selection, radio button number
     ColorSchemeGroup.ItemIndex:=StrToIntDef(vConfigurations.ReadString('KMLSettings', 'Selection',''),0);

     StatusLine.Text:='Waiting to convert .dat file, press Select and Convert';

     DrawImage();

end;

procedure TForm7.ColorSchemeGroupClick(Sender: TObject);
begin
     vConfigurations.WriteString('KMLSettings', 'Selection',IntToStr(ColorSchemeGroup.ItemIndex));

     DrawImage();
end;

procedure TForm7.DrawImage();
var
  PictureFileName:String;
begin

     case ColorSchemeGroup.ItemIndex of
          0: SelectedTheme:='kmllegendnewatlas';
          1: SelectedTheme:='kmllegendcleardarksky';
     end;
     LegendFilename:= SelectedTheme+'.png';

     PictureFileName:=appsettings.DataDirectory+DirectorySeparator+LegendFilename;

     if (not FileExists(PictureFileName)) then
       MessageDlg ('Legend file does not exist!' + PictureFileName, mtConfirmation,[mbIgnore],0)
     else begin
       SchemeImage.Picture.LoadFromFile(PictureFileName);
     end;

     //Fill WorkingLegend array
     case SelectedTheme of
       'kmllegendnewatlas': begin
           KMLValuesSelected:=copy(KMLValuesNewatlas,0,length(KMLValuesNewatlas));
           KMLColorsSelected:=copy(KMLColorsNewatlas,0,length(KMLColorsNewatlas));
        end;
        'kmllegendcleardarksky': begin
            KMLValuesSelected:=copy(KMLValuesCleardarksky,0,length(KMLValuesCleardarksky));
            KMLColorsSelected:=copy(KMLColorsCleardarksky,0,length(KMLColorsCleardarksky));
        end;
     end;

end;

procedure TForm7.SelectAndConvertButtonClick(Sender: TObject);
var
  Infile: TStringList;
  OutFileName, SourceFileName:String;
  MessageString: String;
  AllowFlag: Boolean=False;
  i:integer; //general purpose counter
  pieces: TStringList;
  OutFile: TextFile;
  Darkness: Double;

    // Colors converted from the tool at  http://www.netdelight.be/kml/index.php
  //   transparency byte should be FF.

  ColorPointer:Integer;
  //ColorString:String;
  ColorValue:Double;

  s: String; //Temporary string
  DataLine: String = ''; //Contains data descriptions
  DataStart:Integer = 0; //Starting line of data.

  MSASField: Integer = -1; //Field that contains the MSAS variable, -1 = not defined  yet.
  LatitudeField: Integer = -1; //Field that contains the Latitude variable, -1 = not defined  yet.
  LongitudeField: Integer = -1; //Field that contains the Longitude variable, -1 = not defined  yet.
  MinFieldCount:Integer = 0; //Number of fields required to get a result

begin

     Infile := TStringList.Create;
     pieces := TStringList.Create;

     OpenLogDialog.InitialDir:= appsettings.LogsDirectory;
     OpenLogDialog.Filter:='data file|*.dat';
     if OpenLogDialog.Execute then begin
           StatusMessage('DAT to KML tool started.');

           //CopyLegend image file so that the .kml file can refer to it.
           //It should be OK to overwrite existing file for each run in case the original was changed.
           SourceFileName:=appsettings.DataDirectory+DirectorySeparator+LegendFilename;
           if (not FileExists(SourceFileName)) then
             MessageDlg ('Legend file does not exist!' + SourceFileName, mtConfirmation,[mbIgnore],0)
           else begin
             CopyFile(SourceFileName,appsettings.LogsDirectory+ DirectorySeparator+LegendFilename);
           end;

           Infile.LoadFromFile(OpenLogDialog.Filename);

           //Get Data Line, begins with # UTC Date & Time

           for s in Infile do begin
               if AnsiContainsStr(s,'# UTC Date & Time') then begin

                 DataLine:=s;

                 { Parse field definition line for MSAS field. }
                 pieces.Delimiter := ',';
                 pieces.StrictDelimiter := True; //Do not parse spaces also
                 pieces.DelimitedText := s;

                 { Get the field locations. }
                 for i:=0 to pieces.Count-1 do begin
                   if AnsiContainsStr(pieces.Strings[i],'MSAS') then begin
                     MSASField:=i;
                     MinFieldCount:=MaxValue([MinFieldCount, MSASField]);
                   end;
                   if AnsiContainsStr(pieces.Strings[i],'Latitude') then begin
                     LatitudeField:=i;
                     MinFieldCount:=MaxValue([MinFieldCount, LatitudeField]);
                   end;
                   if AnsiContainsStr(pieces.Strings[i],'Longitude') then begin
                     LongitudeField:=i;
                     MinFieldCount:=MaxValue([MinFieldCount, LongitudeField]);
                   end;
                 end;

               end;


               if AnsiStartsStr('#',s) then Inc(DataStart);
           end;

           //writeln(Infile[DataStart]); //debug print first line of data

           //Check that dat file has GPS coordinates:
           if Infile.Count >=DataStart then begin //Check that at least one record has been made.
              if AnsiContainsStr(DataLine,'Latitude') then begin

                 //Set delimeter type for data lines
                 pieces.Delimiter := ';';

                 //Check that kml file does not already exist
                 OutFileName:=appsettings.LogsDirectory+ DirectorySeparator+LazFileUtils.ExtractFileNameOnly(OpenLogDialog.Filename)+'.kml';
                 if FileExists(OutFileName) then begin
                   MessageString:=OutFileName+ 'exists ';
                   StatusMessage(MessageString);
                   case QuestionDlg('Output file exists',MessageString,mtCustom,[mrOK,'Overwrite',mrCancel,'Cancel'],'') of
                        mrOK: begin
                              AllowFlag:=True; //User allowed overwriting file.
                              StatusMessage('KML file ('+OutFileName+') exists, user allowed overwriting.');
                        end;
                        mrCancel: begin
                              StatusMessage('KML file ('+OutFileName+') exists already, user cancelled overwrite.');
                          end;
                   end;
                 end
                 else AllowFlag:=True; //File did not exist, allow writing.

                 if AllowFlag then begin
                   AssignFile(OutFile, OutFileName);
                   Rewrite(OutFile); //Open file for writing
                   writeln(OutFile, '<?xml version="1.0" encoding="UTF-8"?>');
                   writeln(OutFile, '<kml xmlns="http://www.opengis.net/kml/2.2">');
                   writeln(OutFile, '<Document>');

                   //Overlay section
                   writeln(OutFile, '<ScreenOverlay><name>Legend: MPSAS</name><Icon> <href>'+LegendFilename+'</href></Icon>');
                   writeln(OutFile, '<overlayXY x="0" y="0" xunits="fraction" yunits="fraction"/>');
                   writeln(OutFile, '<screenXY x="20" y="45" xunits="pixels" yunits="pixels"/>');
                   writeln(OutFile, '<rotationXY x="0.5" y="0.5" xunits="fraction" yunits="fraction"/>');
                   writeln(OutFile, '<size x="0" y="0" xunits="pixels" yunits="pixels"/>');
                   writeln(OutFile, '</ScreenOverlay>');

                   //Write color map
                   for ColorPointer := low(KMLValuesSelected) to high(KMLValuesSelected) do begin
                     ColorValue:=KMLValuesSelected[ColorPointer];
                     //ColorString:=KMLColorsSelected[ColorPointer];
                     //Style Map (contains Normal and Highlight definitions)
                     writeln(OutFile, format('<StyleMap id="m%.2f"><Pair><key>normal</key><styleUrl>#sn%.2f</styleUrl></Pair><Pair><key>highlight</key><styleUrl>#sh%.2f</styleUrl></Pair></StyleMap>'
                       ,[ColorValue, ColorValue, ColorValue]));

                     //Style Normal (definition of normally displayed icon)
                     writeln(OutFile, format('<Style id="sn%.2f"><IconStyle><color>%s</color>',[ColorValue,KMLColorsSelected[ColorPointer]]));
                     writeln(OutFile, '<scale>1.1</scale><Icon><href>http://maps.google.com/mapfiles/kml/paddle/wht-blank.png</href></Icon><hotSpot x="32" y="1" xunits="pixels" yunits="pixels"/></IconStyle><LabelStyle></LabelStyle><ListStyle><ItemIcon><href>http://maps.google.com/mapfiles/kml/paddle/wht-blank-lv.png</href></ItemIcon></ListStyle></Style>');

                     //Style Highlight (definition of highlited icon)
                     writeln(OutFile, format('<Style id="sh%.2f"><IconStyle><color>%s</color>',[ColorValue,KMLColorsSelected[ColorPointer]]));
                     writeln(OutFile, '<scale>1.3</scale><Icon><href>http://maps.google.com/mapfiles/kml/paddle/wht-blank.png</href></Icon><hotSpot x="32" y="1" xunits="pixels" yunits="pixels"/></IconStyle><LabelStyle></LabelStyle><ListStyle><ItemIcon><href>http://maps.google.com/mapfiles/kml/paddle/wht-blank-lv.png</href></ItemIcon></ListStyle></Style>');
                   end;
                   //Convert GPS coordinates to KML points
                   for i:=DataStart to Infile.Count-1 do begin
                       pieces.DelimitedText:=Infile[i];
                       if (pieces.Count < (MinFieldCount+1)) then begin
                         StatusMessage('DAT fields less than '+IntToStr(MinFieldCount+1)+', no GPS data found.');
                         break;
                       end;
                       writeln(OutFile, '<Placemark>');
                       writeln(OutFile, Format(' <name>%s</name>',[pieces[MSASField]]));

                       //Colorize the marker
                       Darkness:=StrToFloatDef(pieces[MSASField],0);
                       for ColorPointer := low(KMLValuesSelected) to high(KMLValuesSelected) do begin
                         if Darkness > KMLValuesSelected[ColorPointer] then break;
                       end;
                       writeln(OutFile, Format('<styleUrl>#m%.2f</styleUrl>',[KMLValuesSelected[ColorPointer]]));

                       writeln(OutFile, Format(' <Point><coordinates>%s,%s</coordinates></Point>',[pieces[LongitudeField],pieces[LatitudeField]]));
                       writeln(OutFile, '</Placemark>');
                   end;
                   writeln(OutFile, '</Document>');
                   writeln(OutFile, '</kml>');
                   StatusMessage('KML file ('+OutFileName+') written.');
                   CloseFile(OutFile);

                 end;

              end
              else begin
                MessageString:='There was no GPS data stored in '+OpenLogDialog.Filename;
                StatusMessage(MessageString);
                MessageDlg('No GPS data in file',MessageString,mtWarning,[mbOK],'');
              end;
           end else begin
              MessageString:='The dat file is too short: '+OpenLogDialog.Filename;
              StatusMessage(MessageString);
              MessageDlg('dat file too short',MessageString,mtWarning,[mbOK],'');
           end;



      end;
     Infile.Free;

     StatusLine.Text:='Converted file stored in: '+OutFileName;

end;

initialization
  {$I dattokml.lrs}


end.


