#SWMM5 - Delphi GUi Code for Making Summary Tables

unit Fresults;

{-------------------------------------------------------------------}
{                    Unit:    Fresults.pas                          }
{                    Project: EPA SWMM                              }
{                    Version: 5.1                                   }
{                    Date:    1/3/13    (5.1.000)                   }
{                             8/19/14   (5.1.007)                   }
{                             04/01/15  (5.1.008)                   }
{                    Author:  L. Rossman                            }
{                                                                   }
{   MDI child form that displays summary results generated from     }
{   a run of SWMM.                                                  }
{                                                                   }
{   5.1.008 - module was completely re-written.                     }
{-------------------------------------------------------------------}

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
  System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs,
  Vcl.Grids, Vcl.StdCtrls, Vcl.CheckLst, Vcl.ExtCtrls, Vcl.Themes,
  System.StrUtils, ClipBrd;

type
  TTopic = (Runoff, LID, Groundwater, Washoff, NodeDepth, NodeInflow,
            NodeSurcharge, NodeFlooding, StorageVolume, OutfallLoading,
            LinkFlow, FlowClass, ConduitSurcharge, Pumping, LinkLoad);

type
  TResultsForm = class(TForm)
    TopPanel: TPanel;
    Panel1: TPanel;
    TopicsListBox: TComboBox;
    TheGrid: TStringGrid;
    Panel3: TPanel;
    Label1: TLabel;
    Edit1: TEdit;
    Label2: TLabel;
    procedure FormCreate(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure TopicsListBoxClickCheck(Sender: TObject);
    procedure TheGridDrawCell(Sender: TObject; ACol, ARow: Integer; Rect: TRect;
      State: TGridDrawState);
    procedure TheGridFixedCellClick(Sender: TObject; ACol, ARow: Integer);
  private
    { Private declarations }
    function  Col1IsText(Topic: TTopic): Boolean;
    procedure CopyToString(const Fname: String);
    procedure FindTopicsInReportFile;
    function  GetNewTopic(Line: String; Topic: Integer): Integer;
    procedure GetHeaderLines(Topic: Integer);
    procedure ParseColHeader(const S: String; var H: array of String);
    procedure RefreshTopic;
    procedure SetColHeaders(Topic: TTopic);
  public
    { Public declarations }
    procedure CopyTo;
    function  RefreshReport: Boolean;
  end;

var
  ResultsForm: TResultsForm;

implementation

{$R *.dfm}

uses
  Dcopy, Fmain, Uproject, Ubrowser, Uglobals, Uutils;

const
  Caption1: String =
    'Click a column header to sort the column. ';
  Caption2: String =
    'Flow class frequencies are fraction of all time steps. ';
  TypeLabel: String = #10#10#10'Type';
  LastTopic = 14;

  TopicLabels: array[0..LastTopic] of String =
    ('Subcatchment Runoff',
     'LID Performance',
     'Groundwater',
     'Subcatchment Washoff',
     'Node Depth',
     'Node Inflow',
     'Node Surcharge',
     'Node Flooding',
     'Storage Volume',
     'Outfall Loading',
     'Link Flow',
     'Flow Classification',
     'Conduit Surcharge',
     'Pumping',
     'Link Pollutant Load');

  // Nominal object types for each report topic
  ObjectType: array[0..LastTopic] of Integer =
    (SUBCATCH, SUBCATCH, SUBCATCH, SUBCATCH,
     JUNCTION, JUNCTION, JUNCTION, JUNCTION, JUNCTION, JUNCTION,
     CONDUIT, CONDUIT, CONDUIT, CONDUIT, CONDUIT);

var
  Topics: array[0..LastTopic] of TTopic;
  TopicStart: array[0..LastTopic] of Integer;
  TopicSize: array[0..LastTopic] of Integer;
  TopicHeaderLines: array[0..LastTopic, 0..3] of String;
  CopiedHeaders: array[0..3] of String;
  ColHeaders: array of String;
  ColSorted: array of Integer;
  SortedCol: Integer;
  CurrentTopic: TTopic;
  LineCount: Integer;
  UpdateCount: Boolean;
  FixedCellColor: TColor;
  F: TextFile;

procedure TResultsForm.FormCreate(Sender: TObject);
//-----------------------------------------------------------------------------
// Form's OnCreate handler.
//-----------------------------------------------------------------------------
var
  W: Integer;
begin
  Label1.Caption := Caption1;
  W := Canvas.TextWidth('W');
  with TheGrid do
  begin
    Align := alClient;
    ColCount := 11;
    FixedCellColor := FixedColor;
    DefaultColWidth := 8 * W;
    ColWidths[0] := 12 * W;
    ColWidths[1] := 8 * W;
    DefaultRowHeight := Edit1.Height - 2;
    RowHeights[0] := 4 * (DefaultRowHeight - 4);
  end;
  TheGrid.Visible := False;
  TopPanel.Visible := False;
  TopicsListBox.ItemHeight := Uglobals.ItemHeight;
  CurrentTopic := Runoff;
end;

procedure TResultsForm.FormClose(Sender: TObject; var Action: TCloseAction);
//-----------------------------------------------------------------------------
// Form's OnClose handler.
//-----------------------------------------------------------------------------
begin
  SetLength(ColHeaders, 0);
  SetLength(ColSorted, 0);
  Action := caFree;
end;

function TResultsForm.RefreshReport: Boolean;
//-----------------------------------------------------------------------------
// Refreshes the form when new simulation results are obtained.
//-----------------------------------------------------------------------------
var
  I, J, K: Integer;
begin

  // Clear all report topic info
  Result := False;
  TopicsListBox.Clear;
  for I := 0 to High(Topics) do
  begin
    TopicStart[I] := -1;
    TopicSize[I] := 0;
    for J := 0 to 3 do TopicHeaderLines[I][J] := '';
  end;

  // Locate report topics in the SWMM report file
  FindTopicsInReportFile;

  // Add labels for found topics to TopicsListBox
  K := -1;
  for I := 0 to High(Topics) do
  begin
    if TopicStart[I] >= 0 then
    begin
      Inc(K);
      Topics[K] := TTopic(I);
      TopicsListBox.Items.Add(TopicLabels[I]);
    end;
  end;

  if TopicsListBox.Items.Count = 0 then
  begin
    Label1.Caption := 'There are no results to display.';
    TopicsListBox.Visible := False;
    TheGrid.Visible := False;
    exit;
  end;

  // Set the current selected topic
  if Ord(CurrentTopic) > K  then CurrentTopic := Runoff;
  if K >= 0 then
  begin
   TopicsListBox.ItemIndex := Ord(CurrentTopic);
   RefreshTopic;
  end;

  TopPanel.Visible := True;
  TheGrid.Visible := True;
  Result := True;
end;

procedure TResultsForm.TheGridDrawCell(Sender: TObject; ACol, ARow: Integer;
  Rect: TRect; State: TGridDrawState);
//-----------------------------------------------------------------------------
// String grid's OnDrawCell handler.
//-----------------------------------------------------------------------------
var
  x : Integer;
  s : String;
  offset: Integer;

begin
  offset := Canvas.TextWidth('0');
  with Sender as TStringGrid do
  begin
    // Fill background of cell
    if (ARow < FixedRows) or (ACol < FixedCols) then
    begin
      if StyleServices.Enabled then
      begin
        Canvas.Brush.Color := StyleServices.GetStyleColor(scPanel);
        Rect.Left := Rect.Left - 4;
      end;
    end;
    Canvas.FillRect(Rect);

    // Draw contents of header row
    if (ARow = 0) then
    begin

      // Use bold font for sorted column
      Canvas.Font.Style := [];
      if (ACol = SortedCol) and (ColSorted[ACol] <> 0)
      then Canvas.Font.Style := [fsBold];

      // Use Windows API DeawText function to draw multi-line header
      InflateRect(Rect, 0, -2);
      DrawText(Canvas.Handle, PChar(ColHeaders[ACol]), -1, Rect,
        DT_CENTER or DT_WORDBREAK);
      Canvas.Font.Style := [];
    end

    // Draw cell value for body of table
    else
    begin
      // Use left alignment if first column non-numeric
      if ((ACol = 1) and Col1IsText(CurrentTopic))
      or (ACol = 0) then
      begin
        SetTextAlign(Canvas.Handle, TA_LEFT);
        x := Rect.Left + offset;
      end

      // Other columns are aligned right
      else
      begin
        SetTextAlign(Canvas.Handle, TA_RIGHT);
        x := Rect.Right - offset;
      end;

      // Draw the string value in the cell
      SetBkMode(Canvas.Handle, TRANSPARENT);
      s := Cells[ACol, ARow];
      Canvas.TextOut(x, Rect.Top+2, s);
    end;
  end;
end;

procedure TResultsForm.TheGridFixedCellClick(Sender: TObject; ACol, ARow: Integer);
//-----------------------------------------------------------------------------
// OnFixedCellClick handler for the StringGrid.
//-----------------------------------------------------------------------------
var
  Numeric: Boolean;
  ObjType: Integer;
  ObjName: String;
  FoundObject: Integer;
  FoundIndex:  Integer;
  Found: Boolean;
begin
  // Detect if an object name was clicked
  if (ACol = 0) and (Arow > 0) then
  begin
    ObjType := ObjectType[Integer(Topics[Ord(CurrentTopic)])];
    ObjName := TheGrid.Cells[ACol, ARow];

    Found := False;
    if Project.IsSubcatch(ObjType)
    then Found := Project.FindSubcatch(ObjName, FoundObject, FoundIndex)
    else if Project.IsNode(ObjType)
    then Found := Project.FindNode(ObjName, FoundObject, FoundIndex)
    else if Project.IsLink(ObjType)
    then Found := Project.FindLink(ObjName, FoundObject, FoundIndex);
    if Found then
    begin
      Ubrowser.BrowserUpdate(FoundObject, FoundIndex);
      TheGrid.Row := Arow;
    end;
    exit;
  end;

  // Otherwise sort the column clicked on.
  // Determine if column holds numeric or text values.
  Numeric := True;
  if (ACol = 0) or
  ( (ACol = 1) and Col1IsText(CurrentTopic) ) then Numeric := False;

  // Update the sorted state of the column
  if ColSorted[ACol] = 0 then ColSorted[ACol] := 1
  else ColSorted[ACol] := - ColSorted[ACol];
  SortedCol := ACol;

  // Sort the grid by the entries in the selected column
  Uutils.SortStringGrid(TheGrid, ACol, Numeric, ColSorted[ACol]);
end;

procedure TResultsForm.TopicsListBoxClickCheck(Sender: TObject);
//-----------------------------------------------------------------------------
// OnClickCheck handler for the Topics combobox - displays the summary
// results for the selected topic.
//-----------------------------------------------------------------------------
begin
  with TopicsListBox do
  begin
    // Display report if new topic selected
    if ItemIndex <> Integer(CurrentTopic) then
    begin
      CurrentTopic := TTopic(ItemIndex);
      RefreshTopic;
      TheGrid.Row := 1;
      TheGrid.Col := 1;
    end;
  end;
end;

procedure TResultsForm.FindTopicsInReportFile;
//-----------------------------------------------------------------------------
// Finds the starting line and number of lines for each topic contained
// in the SWMM report file.
//-----------------------------------------------------------------------------
var
  K: Integer;
  Line : String;
  Topic: Integer;
begin
  // Open SWMM's report file
  if FileExists(TempReportFile) then
  begin
    AssignFile(F, TempReportFile);

    // Initialize line count and current topic
    LineCount := 0;
    Topic := -1;

    // Turn off updating of topic's size
    UpdateCount := False;

    // Read each line of file
    try
      {$I-}
      Reset(F);
      {$I+}
      if IOResult = 0 then
      while not Eof(F) do
      begin
        Readln(F, Line);
        Inc(LineCount);

        // Check if current line starts a new report topic
        K := GetNewTopic(Line, Topic);

        // New topic found -- parse its header lines
        if K >= 0 then
        begin
          Topic := K;
          GetHeaderLines(Topic);
        end

        // Otherwise, if still updating size of current topic
        else if (Topic >= 0) and UpdateCount then
        begin

          // Line not blank nor end of current topic -
          // add to size of current topic
          Line := Trim(Line);
          if (Length(Line) > 0) and not (AnsiStartsStr('-', Line))
          then Inc(TopicSize[Topic])

          // Otherwise at end of current topic -- stop updating its size
          else
          begin
           UpdateCount := False;
          end;
        end;
      end;

    // Close report file
    finally
      CloseFile(F);
    end;
  end;
end;

function TResultsForm.GetNewTopic(Line: String; Topic: Integer): Integer;
//-----------------------------------------------------------------------------
// Checks if current line from SWMM report file starts a new topic.
//-----------------------------------------------------------------------------
var
  I: Integer;
begin
  Result := -1;
  for I := Topic+1 to High(TopicLabels) do
  begin
    if ContainsText(Line, TopicLabels[I] + ' Summary') then
    begin
      Result := I;
      break;
    end;
  end;
end;

procedure TResultsForm.GetHeaderLines(Topic: Integer);
//-----------------------------------------------------------------------------
// Saves the lines from the SWMM report file that constitute the
// column headings for the current report topic.
// Headings have following format:
//  Separator Line (-----)
//  Heading Line 1
//  . . .
//  Separator Line (------)
//-----------------------------------------------------------------------------
var
  Line: String;
  I: Integer;
begin
  // Turn off updating of topic size
  UpdateCount := False;
  I := 0;

  // Skip next two lines after topic title
  Readln(F, Line);
  Inc(LineCount);
  Readln(F, Line);
  Inc(LineCount);

  // Exit if start of header section not found within next 2 lines
  Readln(F, Line);
  Inc(LineCount);
  if not AnsiStartsStr('  -', Line) then
  begin
    Readln(F, Line);
    Inc(LineCount);
    if not AnsiStartsStr('  -', Line) then Exit;
  end;

  // Keep reading header lines until last header line found
  // (Headers can't have more than 4 lines of text)
  while (I <= 4)  do
  begin
    Readln(F, Line);
    Inc(LineCount);

    // Last line of header section found
    if AnsiStartsStr('  -', Line) then
    begin
      // Make topic start here
      TopicStart[Topic] := LineCount;
      UpdateCount := True;
      break;
    end

    // Otherwise copy line to topic's header
    else if I < 4 then
    begin
      TopicHeaderLines[Topic][I] := Line;
      Inc(I);
    end;
  end;
end;

procedure TResultsForm.RefreshTopic;
//-----------------------------------------------------------------------------
// Displays summary results for the currently selected topic.
//-----------------------------------------------------------------------------
var
  I, J, N: Integer;
  Topic: TTopic;
  Line : String;
  Caption: String;
  Tokens: TStringList;
begin
  // Convert from index of available topics to absolute topic index
  Topic := Topics[Ord(CurrentTopic)];
  SortedCol := -1;

  // Clear contents of the report table grid
  for I := 0 to TheGrid.RowCount - 1 do TheGrid.Rows[I].Clear;

  // Assign caption text for topic
  Caption := Caption1;
  if Topic = FlowClass then Caption := Caption + Caption2;  // Flow Classiifcation topic
  Label1.Caption := Caption;

  // Provide a minimum size grid for topics with no results
  if TopicSize[Ord(Topic)] = 0 then
  begin
    TheGrid.FixedRows := 1;
    TheGrid.RowCount := 2;
  end

  // Otherwise open the SWMM report file
  else if FileExists(Uglobals.TempReportFile) then
  begin
    // Set number of rows in the grid
    TheGrid.FixedRows := 1;
    TheGrid.RowCount := TopicSize[Ord(Topic)] + TheGrid.FixedRows;

    // Set text of column headers
    SetColHeaders(Topic);

    // Re-size and refresh the grid
    TheGrid.ColCount := Length(ColHeaders);
    SetLength(ColSorted, TheGrid.ColCount);
    for I := 0 to TheGrid.ColCount - 1 do ColSorted[I] := 0;

    // Open the SWMM report file
    Tokens := TStringList.Create;
    AssignFile(F, TempReportFile);
    try
      {$I-}
      Reset(F);
      {$I+}
      if IOResult = 0 then
      begin

        // Move file to start of current topic's report
        for I := 1 to TopicStart[Ord(Topic)] do Readln(F, Line);

        // Tokenize each line of topic's results and insert into grid
        for I := 1 to TopicSize[Ord(Topic)] do
        begin
          Readln(F, Line);
          Uutils.Tokenize(Line, Tokens, N);
          for J := 0 to N - 1 do TheGrid.Cells[J,I] := Tokens[J];
        end;
        TheGrid.Refresh;
      end;

    // Close SWMM report file
    finally
      Tokens.Free;
      CloseFile(F);
    end;
  end;
end;

procedure TResultsForm.SetColHeaders(Topic: TTopic);
//-----------------------------------------------------------------------------
//  Forms the multi-line column headers that appear in the grid table
//  for a specific report topic.
//-----------------------------------------------------------------------------
var
  I, N, T: Integer;
  Tokens: TStringList;
  Units1, Units2, Units3, Text1: String;
begin
  T := Ord(Topic);
  case Topic of
    Runoff: // Subcatchment Runoff (3 lines with 9 cols)
    begin
      // Get units appearing on 3rd header line
      Units1 := Copy(TopicHeaderLines[T][2], 32, 2);
      Units2 := Copy(TopicHeaderLines[T][2], 81, 9);
      Units3 := Copy(TopicHeaderLines[T][2], 95, 4);

      // Build up each column heading
      SetLength(ColHeaders, 9);
      ColHeaders[0] := #10#10#10'Subcatchment';
      ColHeaders[1] := #10'Total'#10'Precip'#10 + Units1;
      ColHeaders[2] := #10'Total'#10'Runon'#10 + Units1;
      ColHeaders[3] := #10'Total'#10'Evap'#10 + Units1;
      ColHeaders[4] := #10'Total'#10'Infil'#10 + Units1;
      ColHeaders[5] := #10'Total'#10'Runoff'#10 + Units1;
      ColHeaders[6] := #10'Total'#10'Runoff'#10 + Units2;
      ColHeaders[7] := #10'Peak'#10'Runoff'#10 + Units3;
      ColHeaders[8] := #10#10'Runoff'#10'Coeff';
      CopiedHeaders[0] := Format('%-16s',[' ']);
    end;

    LID: // LID Performance
    begin
      Units1 := Trim(Copy(TopicHeaderLines[T][2], 44, 3));
      SetLength(ColHeaders, 10);
      ColHeaders[0] := #10#10#10'Subcatchment';
      ColHeaders[1] := #10#10#10'LID Control';
      ColHeaders[2] := #10'Total'#10'Inflow'#10 + Units1;
      ColHeaders[3] := #10'Evap'#10'Loss'#10 + Units1;
      ColHeaders[4] := #10'Infil'#10'Loss'#10 + Units1;
      ColHeaders[5] := #10'Surface'#10'Outflow'#10 + Units1;
      ColHeaders[6] := #10'Drain'#10'Outflow'#10 + Units1;
      ColHeaders[7] := #10'Initial'#10'Storage'#10 + Units1;
      ColHeaders[8] := #10'Final'#10'Storage'#10 + Units1;
      ColHeaders[9] := #10'Continuity'#10'Error'#10'%';
    end;

    Groundwater:  // Groundwater statistics
    begin
      Units1 := Copy(TopicHeaderLines[T][3], 30, 2);
      Units2 := Copy(TopicHeaderLines[T][3], 65, 3);
      Units3 := Copy(TopicHeaderLines[T][3], 84, 2);
      SetLength(ColHeaders, 10);
      ColHeaders[0] := #10#10#10'Subcatchment';
      ColHeaders[1] := #10'Total'#10'Infil'#10 + Units1;
      ColHeaders[2] := #10'Total'#10'Evap'#10 + Units1;
      ColHeaders[3] := 'Total'#10'Lower'#10'Seepage'#10 + Units1;
      ColHeaders[4] := 'Total'#10'Lateral'#10'Outflow'#10 + Units1;
      ColHeaders[5] := 'Maximum'#10'Lateral'#10'Outflow'#10 + Units2;
      ColHeaders[6] := 'Average'#10'Upper'#10'Moisture'#10;
      ColHeaders[7] := 'Average'#10'Water'#10'Table'#10 + Units3;
      ColHeaders[8] := 'Final'#10'Upper'#10'Moisture'#10;
      ColHeaders[9] := 'Final'#10'Water'#10'Table'#10 + Units3;
    end;

    Washoff: // Washoff Loads
    begin
      Tokens := TStringList.Create;
      try
        // Tokenize header with pollutant names
        Uutils.Tokenize(TopicHeaderLines[T][0], Tokens, N);

        // Set number of header columns to number of pollutant names
        // + one column for subcatchment name
        SetLength(ColHeaders, N+1);

        // Fill in first line of grid column headers
        ColHeaders[0] := #10#10#10;
        for I := 0 to N-1 do ColHeaders[I+1] := #10#10 + Tokens[I] + #10;

        // Fill in second line of column headers (with mass units)
        Uutils.Tokenize(TopicHeaderLines[T][1], Tokens, N);
        for I := 0 to N-1 do ColHeaders[I] := ColHeaders[I] + Tokens[I];
      finally
        Tokens.Free;
      end;
    end;

    NodeDepth: // Node Depth
    begin
      Units1 := Trim(Copy(TopicHeaderLines[T][2], 35, 6));
      SetLength(ColHeaders, 8);
      ColHeaders[0] := #10#10#10'Node';
      ColHeaders[1] := TypeLabel;
      ColHeaders[2] := #10'Average'#10'Depth'#10 + Units1;
      ColHeaders[3] := #10'Maximum'#10'Depth'#10 + Units1;
      ColHeaders[4] := #10'Maximum'#10'HGL'#10 + Units1;
      ColHeaders[5] := #10'Day of'#10'Maximum'#10'Depth';
      ColHeaders[6] := #10'Hour of'#10'Maximum'#10'Depth';
      ColHeaders[7] := 'Maximum'#10'Reported'#10'Depth'#10 + Units1;
    end;

    NodeInflow: // Node Inflows
    begin
      Units1 := Trim(Copy(TopicHeaderLines[T][3], 38, 4));
      Units2 := Trim(Copy(TopicHeaderLines[T][3], 68, 8));
      SetLength(ColHeaders, 9);
      ColHeaders[0] := #10#10#10'Node';
      ColHeaders[1] := TypeLabel;
      ColHeaders[2] := 'Maximum'#10'Lateral'#10'Inflow'#10 + Units1;
      ColHeaders[3] := 'Maximum'#10'Total'#10'Inflow'#10 + Units1;
      ColHeaders[4] := #10'Day of'#10'Maximum'#10'Inflow';
      ColHeaders[5] := #10'Hour of'#10'Maximum'#10'Inflow';
      ColHeaders[6] := 'Lateral'#10'Inflow'#10'Volume'#10 + Units2;
      ColHeaders[7] := 'Total'#10'Inflow'#10'Volume'#10 + Units2;
      ColHeaders[8] := 'Flow'#10'Balance'#10'Error'#10'Percent';
    end;

    NodeSurcharge: // Node Surcharge
    begin
      Units1 := Trim(Copy(TopicHeaderLines[T][2], 53, 6));
      SetLength(ColHeaders, 5);
      ColHeaders[0] := #10#10#10'Node';
      ColHeaders[1] := TypeLabel;
      ColHeaders[2] := #10#10'Hours'#10'Surcharged';
      ColHeaders[3] := 'Max Height'#10'Above'#10'Crown'#10 + Units1;
      ColHeaders[4] := 'Min Depth'#10'Below'#10'Rim'#10 + Units1;
    end;

    NodeFlooding: // Node Flooding
    begin
      Units1 := Trim(Copy(TopicHeaderLines[T][3], 38, 4));
      Units2 := Trim(Copy(TopicHeaderLines[T][3], 59, 9));
      Units3 := Trim(Copy(TopicHeaderLines[T][3], 69, 9));
      Text1 := Trim(AnsiRightStr(TopicHeaderLines[T][2], 7));
      SetLength(ColHeaders, 7);
      ColHeaders[0] := #10#10#10'Node';
      ColHeaders[1] := #10#10'Hours'#10'Flooded';
      ColHeaders[2] := #10'Maximum'#10'Rate'#10 + Units1;
      ColHeaders[3] := #10'Day of'#10'Maximum'#10'Flooding';
      ColHeaders[4] := #10'Hour of'#10'Maximum'#10'Flooding';
      ColHeaders[5] := 'Total'#10'Flood'#10'Volume'#10 + Units2;
      ColHeaders[6] := 'Maximum'#10'Ponded'#10 + Text1 + #10 + Units3;
    end;

    StorageVolume: // Storage Volume
    begin
      Units1 := Trim(Copy(TopicHeaderLines[T][2], 25, 9));
      Units2 := Trim(Copy(TopicHeaderLines[T][2], 98, 4));
      SetLength(ColHeaders, 10);
      ColHeaders[0] := #10#10'Storage'#10'Unit';
      ColHeaders[1] := #10'Average'#10'Volume'#10+Units1;
      ColHeaders[2] := #10'Average'#10'Percent'#10'Full';
      ColHeaders[3] := #10'Evap'#10'Percent'#10'Loss';
      ColHeaders[4] := #10'Exfil'#10'Percent'#10'Loss';                        //(5.1.008)
      ColHeaders[5] := #10'Maximum'#10'Volume'#10+Units1;
      ColHeaders[6] := #10'Maximum'#10'Percent'#10'Full';
      ColHeaders[7] := #10'Day of'#10'Maximum'#10'Volume';
      ColHeaders[8] := #10'Hour of'#10'Maximum'#10'Volume';
      ColHeaders[9] := #10'Maximum'#10'Outflow'#10+Units2;
    end;

    OutfallLoading: // Outfall Loading
    begin
       Tokens := TStringList.Create;
      try
        // Tokenize header with pollutant names
        Uutils.Tokenize(TopicHeaderLines[T][1], Tokens, N);

        // Set number of header columns to number of tokens found
        // + one column for outfall name
        SetLength(ColHeaders, N+1);

        // Parse flow and volume units from 3rd header line
        Units1 := Trim(Copy(TopicHeaderLines[T][2], 36, 4));
        Units2 := Trim(Copy(TopicHeaderLines[T][2], 54, 9));

        // Set headers for non-pollutant columns
        ColHeaders[0] := #10#10#10'Outfall Node';
        ColHeaders[1] := #10'Flow'#10'Freq.'#10'Pcnt.';
        ColHeaders[2] := #10'Avg.'#10'Flow'#10 + Units1;
        ColHeaders[3] := #10'Max.'#10'Flow'#10 + Units1;
        ColHeaders[4] := #10'Total'#10'Volume'#10 + Units2;

        if N > 4 then
        begin
          // Add pollutant names to column headers
          for I := 5 to N do
            ColHeaders[I] := #10'Total'#10 + Tokens[I-1] + #10;

          // Trim non-pollutant units text from last header line
          Text1 := AnsiMidStr(TopicHeaderLines[T][2], 63,
            Length(TopicHeaderLines[T][2]));

          // Tokenize the pollutant units from this text
          Uutils.Tokenize(Text1, Tokens, N);

          // Add the pollutant units to the column headers
          for I := 0 to N-1 do
            ColHeaders[I+5] := ColHeaders[I+5] + Tokens[I];
        end;
      finally
        Tokens.Free;
      end;
    end;

    LinkFlow: // Link Flow
    begin
      Units1 := Trim(Copy(TopicHeaderLines[T][2], 38, 4));
      Units2 := Trim(Copy(TopicHeaderLines[T][2], 58, 6));
      SetLength(ColHeaders, 8);
      ColHeaders[0] := #10#10#10'Link';
      ColHeaders[1] := TypeLabel;
      ColHeaders[2] := #10'Maximum'#10'|Flow|'#10 + Units1;
      ColHeaders[3] := #10'Day of'#10'Maximum'#10'Flow';
      ColHeaders[4] := #10'Hour of'#10'Maximum'#10'Flow';
      ColHeaders[5] := #10'Maximum'#10'|Velocity|'#10 + Units2;
      ColHeaders[6] := #10'Max /'#10'Full'#10'Flow';
      ColHeaders[7] := #10'Max /'#10'Full'#10'Depth';
    end;

    FlowClass: // Flow Classification
    begin
      SetLength(ColHeaders, 11);
      ColHeaders[0] := #10#10#10'Conduit';
      ColHeaders[1] := #10'Adjusted/'#10'Actual'#10'Length';
      ColHeaders[2] := #10#10'Fully'#10'Dry';
      ColHeaders[3] := #10#10'Upstrm'#10'Dry';
      ColHeaders[4] := #10#10'Dnstrm'#10'Dry';
      ColHeaders[5] := #10#10'Sub'#10'Critical';
      ColHeaders[6] := #10#10'Super'#10'Critical';
      ColHeaders[7] := #10#10'Upstrm'#10'Critical';
      ColHeaders[8] := #10#10'Dnstrm'#10'Critical';
      ColHeaders[9] := #10'Normal'#10'Flow'#10'Limited';
      ColHeaders[10] := #10#10'Inlet'#10'Control';
    end;

    ConduitSurcharge: // Conduit Surcharge
    begin
      SetLength(ColHeaders, 6);
      ColHeaders[0] := #10#10#10'Conduit';
      ColHeaders[1] := #10'Hours'#10'Both Ends'#10'Full';
      ColHeaders[2] := #10'Hours'#10'Upstream'#10'Full';
      ColHeaders[3] := #10'Hours'#10'Dnstream'#10'Full';
      ColHeaders[4] := 'Hours'#10'Above'#10'Normal'#10'Flow';
      ColHeaders[5] := #10'Hours'#10'Capacity'#10'Limited';
    end;

    Pumping: // Pumping
    begin
      Units1 := Trim(Copy(TopicHeaderLines[T][2], 51, 4));
      Units2 := Trim(Copy(TopicHeaderLines[T][2], 76, 9));
      Units3 := Trim(Copy(TopicHeaderLines[T][2], 89, 6));
      SetLength(ColHeaders, 10);
      ColHeaders[0] := #10#10#10'Pump';
      ColHeaders[1] := #10#10'Percent'#10'Utilized';
      ColHeaders[2] := #10#10'Number of'#10'Start-Ups';
      ColHeaders[3] := #10'Minimum'#10'Flow'#10 + Units1;
      ColHeaders[4] := #10'Average'#10'Flow'#10 + Units1;
      ColHeaders[5] := #10'Maximum'#10'Flow'#10 + Units1;
      ColHeaders[6] := #10'Total'#10'Volume'#10 + Units2;
      ColHeaders[7] := #10'Power'#10'Usage'#10 + Units3;
      ColHeaders[8] := '% Time'#10'Below'#10'Pump'#10'Curve';
      ColHeaders[9] := '% Time'#10'Above'#10'Pump'#10'Curve';
    end;

    LinkLoad:  // Link Loadings
    begin
      Tokens := TStringList.Create;
      try
        // Tokenize header with pollutant names
        Uutils.Tokenize(TopicHeaderLines[T][0], Tokens, N);

        // Set number of header columns to number of tokens found
        // + one column for outfall name
        SetLength(ColHeaders, N+1);

        // Add pollutant names to column headers
        ColHeaders[0] := #10#10#10;
        for I := 0 to N-1 do
            ColHeaders[I+1] := #10#10 + Tokens[I] + #10;

        // Tokenize header with pollutant units
        Uutils.Tokenize(TopicHeaderLines[T][1], Tokens, N);

        // Add the pollutant units to the column headers
        for I := 0 to N-1 do
          ColHeaders[I] := ColHeaders[I] + Tokens[I];
      finally
        Tokens.Free;
      end;
    end;
  end;
end;

procedure TResultsForm.CopyTo;
//-----------------------------------------------------------------------------
// Copies the current topic's table to a file or to the Clipboard.
//-----------------------------------------------------------------------------
var
  CopyToForm: TCopyToForm;
begin
  if not TheGrid.Visible then
  begin
    Uutils.MsgDlg('There are no results to copy.', mtInformation, [mbOK]);
    exit;
  end;

  // Launch the CopyTo dialog form
  CopyToForm := TCopyToForm.Create(self);
  with CopyToForm do
  try

    // Fix the format type to text
    FormatGroup.ItemIndex := 2;
    FormatGroup.Enabled := False;

    // Call CopyToString to do the actual copying
    if ShowModal = mrOK
    then CopyToString(DestFileName);
  finally
    Free;
  end;
end;

procedure TResultsForm.CopyToString(const Fname: String);
//-----------------------------------------------------------------------------
// Copies the current table into a stringlist which is then
// saved to file Fname or to the Clipboard if Fname is blank.
//-----------------------------------------------------------------------------
var
  Slist: TStringList;
  S: String;
  Hrows: array[0..3] of String;
  Hcols: array[0..3] of String;
  I: Integer;
  J: Integer;
begin
  // Create a stringlist to hold each row of table
  Slist := TStringList.Create;
  try
    // Add titles to the stringlist
    Slist.Add(Project.Title);
    Slist.Add(TopicsListBox.Text);

    // Add column headings to the string list
    for I := 0 to 3 do Hrows[I] := '';
    for I := 0 to TheGrid.ColCount-1 do
    begin
      ParseColHeader(ColHeaders[I], Hcols);
      if I = 0 then
      begin
        for J := 0 to 3 do Hrows[J] := Format('%-16s', [Hcols[J]]);
      end
      else
      begin
        for J := 0 to 3 do Hrows[J] := Hrows[J] + #9 +
          Format('%-10s', [Hcols[J]]);
      end;
    end;
    for I := 0 to 3 do Slist.Add(Hrows[I]);

    // Add body of table to the string list
    for I := 1 to TheGrid.RowCount-1 do
    begin
      S := Format('%-16s',[TheGrid.Cells[0,I]]);
      for J := 1 to TheGrid.ColCount-1 do
        S := S + #9 + Format('%-10s', [TheGrid.Cells[J, I]]);
      Slist.Add(S);
    end;

    // Save the string list to file Fname if file name supplied
    if Length(Fname) > 0 then Slist.SaveToFile(Fname)

    // Otherwise place the text of the string list onto the clipboard
    else Clipboard.SetTextBuf(PChar(Slist.Text));

  // Free the string list
  finally
    Slist.Free;
  end;
end;

procedure TResultsForm.ParseColHeader(const S: String; var H: array of String);
//-----------------------------------------------------------------------------
// Separates the multiple lines of header text in S into separate strings
// returned in H.
//-----------------------------------------------------------------------------
var
  I, J, K, N, P: Integer;
  S1: String;
begin
  // Make a copy of the multi-line header
  S1 := S;

  // Initialize index into S1 (K), number of lines in the header (N),
  // and default contents of H
  K := 0;
  N := High(H);
  for I := 0 to N do H[I] := '';

  // For the first N-1 lines of the header
  for J := 0 to N-1 do
  begin
    // Locate the next line feed character
    p := Pos(#10, S1);
    if P = 0 then break;

    // If line feeds are successive, use just a space for the next line
    if P <= K+1 then H[J] := ' '

    // Otherwise copy the text between line feeds into the next line in H
    else H[J] := Copy(S1, K+1, P-K);

    // Move the index to the line feed and replace it with a space
    K := P;
    S1[K] := ' ';
  end;

  // Copy the remaining characters of S1 to the last header line
  H[N] := Copy(S1, K+1, 100);
end;

function TResultsForm.Col1IsText(Topic: TTopic): Boolean;
//-----------------------------------------------------------------------------
// Determines if Topic has numeric data in column 1 of string grid.
//-----------------------------------------------------------------------------
begin
  Result := Topics[Ord(Topic)] in [LID, NodeDepth, NodeInflow, NodeSurcharge,
                                   LinkFlow];
end;

end.

Comments

Popular posts from this blog

World-Renowned Urban Drainage Modeling Authority Robert Dickinson Joined Innovyze in January, 2008

SWMM 5 Error Messages

New Warning Messages in SWMM 5.0.014 to 5.0.016