unit FMX.uecMapillaryComponent;

interface

uses
  System.SysUtils, System.Types, System.UITypes, System.Classes,
  System.Variants,
  FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Objects,
  System.UIConsts,
  FMX.ListBox,
  FMX.Layouts, FMX.Controls.Presentation, FMX.StdCtrls,
  FMX.uecNativeMapControl, FMX.uecNativeShape, FMX.uecMapUtil, FMX.uecMapillary;

type

  TMapillaryComponent = class(TECCustomTool)
  protected

  private
    FMapillaryPanel: TRectangle;
    FPanelSpin, FPanelBar, FPanelSepa, FPanelTop: TRectangle;
    FSequenceColor: TRectangle;

    FGlyphColor: TAlphaColor;

    FBtnTimer,
    FTimer: TTimer;

    FComboCount: TComboBox;

    FRun, FFirst, FNext, FPrev, FLast, FClose: TRectangle;

    FIncrement,FDecInc,
    FButtonSize: integer;

    FImage: TImage;
    FImageSize: integer;

    FMargin: integer;

    FBmp: TBitmap;

    FCenterMapOnImage: boolean;

    FMapillaryLayer: TECMapillaryLayer;
    FMapillaryImage: TMapillaryImage;

    FSequence: TMapillarySequence;
    FImageIndex: integer;

    FSequences: TStringList;

    FOnChange, FOnImage, FOnBeginRequest, FOnEndRequest: TNotifyEvent;

    FDataRunGlyph, FDataPauseGlyph, FHintRunGlyph, FHintPauseGlyph: string;


    procedure doButtonMouseDown(Sender: TObject; Button: TMouseButton;  Shift: TShiftState; X, Y: single);
    procedure doButtonMouseUp(Sender: TObject; Button: TMouseButton;  Shift: TShiftState; X, Y: single);
    procedure doBtnTimer(Sender : TObject);

    function getXYRadius: single;
    procedure setXYRadius(const Value: single);

    function getGlyphData(index: integer): string;
    procedure setGlyphData(index: integer; Value: string);

    function getGlyphHint(index: integer): string;
    procedure setGlyphHint(index: integer; Value: string);

    procedure BuildButton(var FBtn: TRectangle; const APArent: TFmxObject;
      const ASize: integer; ASVGData: string);

    procedure setColor(const Value: TAlphaColor);
    function getColor: TAlphaColor;

    procedure setGlyphColor(const Value: TAlphaColor);

    procedure doSpinEditChange(sender: TObject);

    procedure doClose(sender: TObject);

    procedure doBeginRequest(sender: TObject);
    procedure doEndRequest(sender: TObject);

    procedure SetSequence(const Sequence: TMapillarySequence);
    procedure SetImageIndex(const AValue: integer);

    procedure UpdateImage;

    procedure doOnChange(sender: TObject);

    procedure UpdateSize;
    procedure setMargin(AValue: integer);

    procedure doTimer(sender: TObject);
    procedure StopTimer;

    procedure RunClick(sender: TObject);
    procedure LastClick(sender: TObject);
    procedure FirstClick(sender: TObject);
    procedure NextClick(sender: TObject);
    procedure PrevClick(sender: TObject);

    procedure doMouseLeave(sender: TObject);
    procedure doMouseEnter(sender: TObject);

    function getSequenceColorHeight: integer;
    procedure setSequenceColorHeight(Value: integer);

    function getAccessToken: string;
    procedure setAccessToken(Value: string);

    procedure doOnMapillaryLayerClick(Layer: TECMapillaryLayer; item: TECShape;
      MapillarySequence: TMapillarySequence; MapillaryImageIndex: integer);
  public
    constructor Create(AMap: TNativeMapControl); override;
    destructor Destroy; override;

    property AccessToken: string read getAccessToken write setAccessToken;

    property Bitmap: TBitmap read FBmp;

    property BorderSize : integer read FMArgin write setMargin;

    property CenterMapOnImage: boolean read FCenterMapOnImage
      write FCenterMapOnImage;

    property Color: TAlphaColor read getColor write setColor;
    property ColorGlyph: TAlphaColor read FGlyphColor write setGlyphColor;

    property Image: TMapillaryImage read FMapillaryImage;

    property Increment : integer read FIncrement write FIncrement;

    property MapillaryLayer: TECMapillaryLayer read FMapillaryLayer;

    property CloseGlyph: string index 0 read getGlyphData write setGlyphData;
    property RunGlyph: string index 1 read getGlyphData write setGlyphData;
    property PauseGlyph: string index 2 read getGlyphData write setGlyphData;
    property FirstGlyph: string index 3 read getGlyphData write setGlyphData;
    property PrevGlyph: string index 4 read getGlyphData write setGlyphData;
    property NextGlyph: string index 5 read getGlyphData write setGlyphData;
    property LastGlyph: string index 6 read getGlyphData write setGlyphData;

    property CloseHint: string index 0 read getGlyphHint write setGlyphHint;
    property RunHint: string index 1 read getGlyphHint write setGlyphHint;
    property PauseHint: string index 2 read getGlyphHint write setGlyphHint;
    property FirstHint: string index 3 read getGlyphHint write setGlyphHint;
    property PrevHint: string index 4 read getGlyphHint write setGlyphHint;
    property NextHint: string index 5 read getGlyphHint write setGlyphHint;
    property LastHint: string index 6 read getGlyphHint write setGlyphHint;

    property Sequence: TMapillarySequence read FSequence write SetSequence;
    property SequenceColorHeight: integer read getSequenceColorHeight
      write setSequenceColorHeight;

    property ImageIndex: integer read FImageIndex write SetImageIndex;

    property XYRadius: single read getXYRadius write setXYRadius;

    property OnImage: TNotifyEvent read FOnImage write FOnImage;

    property OnChange: TNotifyEvent read FOnChange write FOnChange;

    property OnBeginRequest: TNotifyEvent read FOnBeginRequest
      write FOnBeginRequest;
    property OnEndRequest: TNotifyEvent read FOnEndRequest write FOnEndRequest;

  end;

implementation

const
  SVG_RUN = 'M716.8 512l-384-256v512z';
  SVG_PAUSE = 'M307.2 256h153.6v512H307.2V256zm256 512h153.6V256H563.2v512z';

procedure TMapillaryComponent.StopTimer;
begin

  TPath(FRun.tagObject).Data.Data := FDataRunGlyph;
  FRun.Hint := FHintRunGlyph;
  FRun.tag := 0;
  FTimer.Enabled := false;
end;

procedure TMapillaryComponent.doOnChange(sender: TObject);
begin

  Component.OnChange := nil;

  UpdateSize;

  if visible and not FMapillaryLayer.visible then
    Map.zoom := 16;

  FMapillaryLayer.visible := visible;

  UpdateImage;

  if not visible then
    StopTimer
  else
  begin
    FMapillaryLayer.SearchBounds(Map.NorthEastLatitude, Map.NorthEastLongitude,
      Map.SouthWestLatitude, Map.SouthWestLongitude);
  end;

  if assigned(FOnChange) then
    FOnChange(Self);

  Component.OnChange := doOnChange;
end;

procedure TMapillaryComponent.setGlyphColor(const Value: TAlphaColor);
begin

  FGlyphColor := Value;

  TPath(FClose.tagObject).Fill.Color := Value;
  TPath(FRun.tagObject).Fill.Color := Value;
  TPath(FFirst.tagObject).Fill.Color := Value;
  TPath(FNext.tagObject).Fill.Color := Value;
  TPath(FPrev.tagObject).Fill.Color := Value;
  TPath(FLast.tagObject).Fill.Color := Value;

  TPath(FClose.tagObject).Stroke.Color := GetShadowColorBy(Value, 32);
  TPath(FRun.tagObject).Stroke.Color := TPath(FClose.tagObject).Stroke.Color;
  TPath(FFirst.tagObject).Stroke.Color := TPath(FClose.tagObject).Stroke.Color;
  TPath(FNext.tagObject).Stroke.Color := TPath(FClose.tagObject).Stroke.Color;
  TPath(FPrev.tagObject).Stroke.Color := TPath(FClose.tagObject).Stroke.Color;
  TPath(FLast.tagObject).Stroke.Color := TPath(FClose.tagObject).Stroke.Color;
end;

procedure TMapillaryComponent.setColor(const Value: TAlphaColor);
begin
  FMapillaryPanel.Fill.Color := Value;
  FPanelBar.Fill.Color := Value;
  FPanelSpin.Fill.Color := Value;
  FClose.Fill.Color := Value;
  FRun.Fill.Color := Value;
  FFirst.Fill.Color := Value;
  FNext.Fill.Color := Value;
  FPrev.Fill.Color := Value;
  FLast.Fill.Color := Value;
  FPanelSepa.Fill.Color := Value;
  FPanelTop.Fill.Color := Value;
end;

function TMapillaryComponent.getColor: TAlphaColor;
begin
  result := FMapillaryPanel.Fill.Color;
end;

procedure TMapillaryComponent.setMargin(AValue: integer);
begin
  FMargin := AValue;

  FPanelTop.Height := 2 * AValue;
  FMapillaryPanel.padding.Left := AValue;
  FMapillaryPanel.padding.Top := 0;
  FMapillaryPanel.padding.Bottom := AValue;
  FMapillaryPanel.padding.Right := AValue;

  FPanelBar.padding.Left := AValue;
  FPanelBar.padding.Right := AValue;
  FPanelBar.padding.Top := 0;
  FPanelBar.padding.Bottom := AValue;

  FPanelBar.Height := FButtonSize + AValue;

  FPanelSepa.Height := AValue;

  UpdateSize;
end;

procedure TMapillaryComponent.UpdateSize;
begin
  FImage.Width := FImageSize;
  FImage.Height := FImageSize;
  FMapillaryPanel.Width := FImageSize + 2 * FMargin;

  FPanelBar.Width := FMapillaryPanel.Width - 2 * FMargin;

  Height := round(FSequenceColor.Height + FBmp.Height + FMargin * 2 +
    FPanelBar.Height);

  FPanelSpin.padding.Left := FPanelSpin.Width / 6;
  FPanelSpin.padding.Right := FPanelSpin.padding.Left;
end;

procedure TMapillaryComponent.UpdateImage;
var
  i: integer;
begin

  if visible and assigned(FSequence) and
    ((FImageIndex > -1) and (FImageIndex < FSequence.Count)) then
  begin

    FMapillaryImage := FSequence[FImageIndex];
    // load image 256*256
    // also url1024 and url2048
    if FMapillaryLayer.LoadMapillaryBitmap(FMapillaryImage.Url256, FBmp) then
    begin

      FSequenceColor.Fill.Color := FSequence.Color;
      FSequenceColor.visible := true;
      FPanelBar.Enabled := true;
      FPanelSpin.visible := true;

      FComboCount.Items.BeginUpdate;
      FComboCount.OnChange := nil;
      FComboCount.Items.Clear;
      for i := 0 to FSequence.Count do
        FComboCount.Items.Add(IntToStr(i));
      FComboCount.itemindex := FImageIndex + 1;
      FComboCount.OnChange := doSpinEditChange;
      FComboCount.Items.EndUpdate;

      FImage.Bitmap.Assign(FBmp);

      UpdateSize;

      FClose.visible := true;
      FClose.position.x := FImage.Width - FClose.Width -
        (FClose.XRadius * 0.10);
      FClose.position.y := -FMargin - (FClose.XRadius * 0.20);

      FPanelSepa.position.y := FImage.Height;
      FPanelTop.visible := true;
      FPanelTop.position.y := 0;

      if FCenterMapOnImage then
        Map.setCenter(FMapillaryImage.Lat, FMapillaryImage.Lng);

      if assigned(FOnImage) then
        FOnImage(Self);

    end;
  end
  else
  begin
    FImage.Bitmap.Assign(nil);
    FBmp.Assign(nil);
    FSequence := nil;
    FImageIndex := -1;
    FPanelTop.visible := false;
    FSequenceColor.visible := false;
    FPanelBar.Enabled := false;
    FMapillaryImage := nil;
    FPanelSpin.visible := false;
    FClose.visible := false;

    if assigned(FOnImage) then
      FOnImage(Self);
  end;

end;

procedure TMapillaryComponent.SetSequence(const Sequence: TMapillarySequence);
begin
  ImageIndex := 0;
  UpdateImage;
end;

procedure TMapillaryComponent.SetImageIndex(const AValue: integer);
begin
  FImageIndex := AValue;
  UpdateImage;
end;

// click on line or marker mapillary image
procedure TMapillaryComponent.doOnMapillaryLayerClick(Layer: TECMapillaryLayer;
  item: TECShape; MapillarySequence: TMapillarySequence;
  MapillaryImageIndex: integer);
begin

  FSequence := MapillarySequence;
  FImageIndex := MapillaryImageIndex;

  UpdateImage;
end;

// show first image of selected sequence
procedure TMapillaryComponent.FirstClick(sender: TObject);
begin
  ImageIndex := 0;
end;

// show last image of selected sequence
procedure TMapillaryComponent.LastClick(sender: TObject);
begin
  if assigned(FSequence) then
    ImageIndex := FSequence.Count - 1;
end;

// show next image of selected sequence
procedure TMapillaryComponent.NextClick(sender: TObject);
begin
  if assigned(FSequence) and (ImageIndex < FSequence.Count - 1) then
    ImageIndex := ImageIndex + 1
  else if FRun.tag = 1 then
    StopTimer;
end;

// show prev image of selected sequence
procedure TMapillaryComponent.PrevClick(sender: TObject);
begin

  if ImageIndex > 0 then
    ImageIndex := ImageIndex - 1;

end;

procedure TMapillaryComponent.doSpinEditChange(sender: TObject);
begin

  if FComboCount.itemindex - 1 <> ImageIndex then
    ImageIndex := FComboCount.itemindex - 1;

end;

// fired before call mapillary
procedure TMapillaryComponent.doBeginRequest(sender: TObject);
begin
  if assigned(FOnBeginRequest) then
    OnBeginRequest(Self);
end;

// fired when mapillary request is done
procedure TMapillaryComponent.doEndRequest(sender: TObject);
var
  i, j: integer;

begin

  FSequences.BeginUpdate;

  FSequences.Clear;

  for i := 0 to FMapillaryLayer.Tiles.Count - 1 do
    for j := 0 to FMapillaryLayer.Tiles[i].Sequences.Count - 1 do
      FSequences.addObject(FMapillaryLayer.Tiles[i].Sequences[j].Sequence_id,
        FMapillaryLayer.Tiles[i].Sequences[j]);

  FSequences.EndUpdate;

  if assigned(FOnEndRequest) then
    OnEndRequest(Self);

end;

function TMapillaryComponent.getSequenceColorHeight: integer;
begin
  result := round(FSequenceColor.Height);
end;

procedure TMapillaryComponent.setSequenceColorHeight(Value: integer);
begin
  FSequenceColor.Height := Value;
end;

procedure TMapillaryComponent.doClose(sender: TObject);
begin
  visible := false;
end;

function TMapillaryComponent.getAccessToken: string;
begin
  result := FMapillaryLayer.AccessToken;
end;

procedure TMapillaryComponent.setAccessToken(Value: string);
begin
  FMapillaryLayer.AccessToken := Value;
end;

procedure TMapillaryComponent.doTimer(sender: TObject);
begin
  FTimer.OnTimer := nil;
  NextClick(Self);
  FTimer.OnTimer := doTimer;
end;

procedure TMapillaryComponent.doMouseLeave(sender: TObject);
begin
  if sender is TRectangle then
    TPath(TRectangle(sender).tagObject).Fill.Color := FGlyphColor;
end;

procedure TMapillaryComponent.doMouseEnter(sender: TObject);
begin
  if sender is TRectangle then
    TPath(TRectangle(sender).tagObject).Fill.Color :=
      GetShadowColorBy(FGlyphColor, 32);
end;

procedure TMapillaryComponent.RunClick(sender: TObject);
begin
  case FRun.tag of

    0:
      begin

        TPath(FRun.tagObject).Data.Data := FDataPauseGlyph;
        FRun.Hint := FHintPauseGlyph;
        FRun.tag := 1;
        FTimer.Enabled := true;

      end;

    1:
      begin
        TPath(FRun.tagObject).Data.Data := FDataRunGlyph;
        FRun.Hint := FHintRunGlyph;
        FRun.tag := 0;
        FTimer.Enabled := false;
      end;

  end;


end;


function TMapillaryComponent.getGlyphHint(index: integer): string;
begin
  case index of
    0:
      result := FClose.Hint;
    1:
      result := FHintRunGlyph;
    2:
      result := FHintPauseGlyph;
    3:
      result := FFirst.Hint;
    4:
      result := FPrev.Hint;
    5:
      result := FNext.Hint;
    6:
      result := FLast.Hint;
  end;
end;

procedure TMapillaryComponent.setGlyphHint(index: integer; Value: string);
begin

  case index of
    0:
      FClose.Hint := Value;
    1:
      begin
        FHintRunGlyph := Value;
        if FRun.tag = 0 then
          FRun.Hint := Value;
      end;
    2:
      begin
        FHintPauseGlyph := Value;
        if FRun.tag = 1 then
          FRun.Hint := Value;
      end;

    3:
      FFirst.Hint := Value;
    4:
      FPrev.Hint := Value;
    5:
      FNext.Hint := Value;
    6:
      FLast.Hint := Value;
  end;

end;



function TMapillaryComponent.getGlyphData(index: integer): string;
begin
  case index of
    0:
      result := TPath(FClose.tagObject).Data.Data;
    1:
      result := FDataRunGlyph;
    2:
      result := FDataPauseGlyph;
    3:
      result := TPath(FFirst.tagObject).Data.Data;
    4:
      result := TPath(FPrev.tagObject).Data.Data;
    5:
      result := TPath(FNext.tagObject).Data.Data;
    6:
      result := TPath(FLast.tagObject).Data.Data;
  end;
end;

procedure TMapillaryComponent.setGlyphData(index: integer; Value: string);
begin

  case index of
    0:
      TPath(FClose.tagObject).Data.Data := Value;
    1:
      begin
        FDataRunGlyph := Value;
        if FRun.tag = 0 then
          TPath(FRun.tagObject).Data.Data := Value;
      end;
    2:
      begin
        FDataPauseGlyph := Value;
        if FRun.tag = 1 then
          TPath(FRun.tagObject).Data.Data := Value;
      end;

    3:
      TPath(FFirst.tagObject).Data.Data := Value;
    4:
      TPath(FPrev.tagObject).Data.Data := Value;
    5:
      TPath(FNext.tagObject).Data.Data := Value;
    6:
      TPath(FLast.tagObject).Data.Data := Value;
  end;

end;

procedure TMapillaryComponent.BuildButton(var FBtn: TRectangle;
  const APArent: TFmxObject; const ASize: integer; ASVGData: string);
var
  APath: TPath;
begin
  FBtn := TRectangle.Create(FMapillaryPanel);
  FBtn.Parent := APArent;
  FBtn.Width := ASize;
  FBtn.Height := ASize;
  FBtn.Stroke.Color := 0;
  FBtn.Cursor := crHandPoint;

  FBtn.OnMouseLeave := doMouseLeave;
  FBtn.OnMouseEnter := doMouseEnter;

  APath := TPath.Create(FBtn);

  FBtn.tagObject := APath;
  APath.Parent := FBtn;
  APath.hitTest := false;

  APath.Data.Data := ASVGData;

  APath.Width := ASize div 2;
  APath.Height := ASize div 2;

  APath.Fill.Color := FGlyphColor;

{$IF CompilerVersion >= 27 }
  APath.Align := TAlignLayout.Center;
{$ELSE}
  APath.Align := TAlignLayout.alCenter;
{$ENDIF}
end;

function TMapillaryComponent.getXYRadius: single;
begin
  result := FMapillaryPanel.XRadius;
end;

procedure TMapillaryComponent.setXYRadius(const Value: single);
var
  maxXY: single;
begin

  if Value <= 10 then
    maxXY := Value
  else
    maxXY := 10;

  FMapillaryPanel.XRadius := maxXY;
  FMapillaryPanel.YRadius := maxXY;
  FClose.XRadius := maxXY;
  FClose.YRadius := maxXY;
  FPanelTop.XRadius := maxXY;
  FPanelTop.YRadius := maxXY;
end;



procedure TMapillaryComponent.doButtonMouseDown(Sender: TObject; Button: TMouseButton;  Shift: TShiftState; X, Y: single);
begin
 if Sender = FNext then
  FDecInc := 1
 else
 if Sender = FPrev then
  FDecInc := -1
 else
  FDecInc := 0;

  FBtnTimer.Enabled := true;
end;

procedure TMapillaryComponent.doButtonMouseUp(Sender: TObject; Button: TMouseButton;  Shift: TShiftState; X, Y: single);
begin
 FBtnTimer.Enabled := false;
 FComboCount.SetFocus;
end;

procedure TMapillaryComponent.doBtnTimer(Sender : TObject);
begin
 if assigned(Image) then
   ImageIndex := ImageIndex + (FDecInc * FIncrement);
end;

constructor TMapillaryComponent.Create(AMap: TNativeMapControl);
begin

  inherited;

  FIncrement := 10;

  FGlyphColor := claBlack;

  FTimer := TTimer.Create(nil);
  FTimer.Enabled := false;
  FTimer.Interval := 100;
  FTimer.OnTimer := doTimer;

  FBtnTimer := TTimer.Create(nil);
  FBtnTimer.Enabled := false;
  FBtnTimer.Interval := 100;
  FBtnTimer.OnTimer  := doBtnTimer;

  FBmp := TBitmap.Create;

  FSequences := TStringList.Create;

  FCenterMapOnImage := true;

  FDataRunGlyph := SVG_RUN;
  FDataPauseGlyph := SVG_PAUSE;

  FHintRunGlyph   := 'Run';
  FHintPauseGlyph := 'Pause';


  // FMapillaryPanel will be the support that determines the total occupancy of our component
  // It will be connected to TECNativeMap

  FMapillaryPanel := TRectangle.Create(nil);

{$IF CompilerVersion >= 27 }
  FMapillaryPanel.Stroke.Kind := TBrushKind.None;
{$ELSE}
  FMapillaryPanel.Stroke.Kind := TBrushKind.bkNone;
{$ENDIF}
  FMapillaryPanel.Fill.Color := claLightgray;
  FMapillaryPanel.showHint := true;

  FButtonSize := 32;

  FPanelBar := TRectangle.Create(FMapillaryPanel);
{$IF CompilerVersion >= 27 }
  FPanelBar.Stroke.Kind := TBrushKind.None;
{$ELSE}
  FPanelBar.Stroke.Kind := TBrushKind.bkNone;
{$ENDIF}
  FPanelBar.Parent := FMapillaryPanel;
{$IF CompilerVersion >= 27 }
  FPanelBar.Align := TAlignLayout.Bottom;
{$ELSE}
  FPanelBar.Align := TAlignLayout.alBottom;
{$ENDIF}
  FPanelBar.Height := FButtonSize;

  FSequenceColor := TRectangle.Create(FMapillaryPanel);

{$IF CompilerVersion >= 27 }
  FSequenceColor.Stroke.Kind := TBrushKind.None;
{$ELSE}
  FSequenceColor.Stroke.Kind := TBrushKind.bkNone;
{$ENDIF}
  FSequenceColor.Parent := FMapillaryPanel;

{$IF CompilerVersion >= 27 }
  FSequenceColor.Align := TAlignLayout.Bottom;
{$ELSE}
  FSequenceColor.Align := TAlignLayout.alBottom;
{$ENDIF}
  FSequenceColor.Height := 4;

  FSequenceColor.position.y := FMapillaryPanel.Height;

  FPanelTop := TRectangle.Create(FMapillaryPanel);
{$IF CompilerVersion >= 27 }
  FPanelTop.Stroke.Kind := TBrushKind.None;
{$ELSE}
  FPanelTop.Stroke.Kind := TBrushKind.bkNone;
{$ENDIF}
  FPanelTop.Corners := [TCorner.TopLeft, TCorner.TopRight];
  FPanelTop.Parent := FMapillaryPanel;

{$IF CompilerVersion >= 27 }
  FPanelTop.Align := TAlignLayout.Top;
{$ELSE}
  FPanelTop.Align := TAlignLayout.alTop;
{$ENDIF}
  FPanelSepa := TRectangle.Create(FMapillaryPanel);
{$IF CompilerVersion >= 27 }
  FPanelSepa.Stroke.Kind := TBrushKind.None;
{$ELSE}
  FPanelSepa.Stroke.Kind := TBrushKind.bkNone;
{$ENDIF}
  FPanelSepa.Parent := FMapillaryPanel;

{$IF CompilerVersion >= 27 }
  FPanelSepa.Align := TAlignLayout.Bottom;
{$ELSE}
  FPanelSepa.Align := TAlignLayout.alBottom;
{$ENDIF}
  FSequenceColor.Height := 4;

  BuildButton(FRun, FPanelBar, FButtonSize, SVG_RUN);
  BuildButton(FFirst, FPanelBar, FButtonSize,
    'M358.989 657.36h51.154V366.64H358.99zM631.348 656.96l36.118-36.29L559.112 511.8l108.354-108.87-36.118-36.29L486.875 511.8z');
  BuildButton(FNext, FPanelBar, FButtonSize,
    'M419.3 264.8l-61.8 61.8L542.9 512 357.5 697.4l61.8 61.8L666.5 512z');
  BuildButton(FPrev, FPanelBar, FButtonSize,
    'M604.7 759.2l61.8-61.8L481.1 512l185.4-185.4-61.8-61.8L357.5 512z');
  BuildButton(FLast, FPanelBar, FButtonSize,
    'M562.19 511.799l-144.6 145.718-36.15-36.43L489.89 511.8l-108.45-109.29 36.15-36.429zM691.2 657.92H640V366.08h51.2z');

  FFirst.Hint := 'First';
  FLast.Hint  := 'Last';
  FPrev.Hint  := 'Previous';
  FNext.Hint  := 'Next';

{$IF CompilerVersion >= 27 }
  FRun.Align := TAlignLayout.Left;
{$ELSE}
  FRun.Align := TAlignLayout.alLeft;
{$ENDIF}
{$IF CompilerVersion >= 27 }
  FFirst.Align := TAlignLayout.Left;
{$ELSE}
  FFirst.Align := TAlignLayout.alLeft;
{$ENDIF}
{$IF CompilerVersion >= 27 }
  FPrev.Align := TAlignLayout.Left;
{$ELSE}
  FPrev.Align := TAlignLayout.alLeft;
{$ENDIF}
{$IF CompilerVersion >= 27 }
  FNext.Align := TAlignLayout.Right;
{$ELSE}
  FNext.Align := TAlignLayout.alRight;
{$ENDIF}
{$IF CompilerVersion >= 27 }
  FLast.Align := TAlignLayout.Right;
{$ELSE}
  FLast.Align := TAlignLayout.alRight;
{$ENDIF}
  FLast.position.x := FPanelBar.Width;
  FFirst.position.x := FButtonSize;

  FRun.OnClick := RunClick;
  FLast.OnClick := LastClick;
  FFirst.OnClick := FirstClick;
  FPrev.OnClick := PrevClick;
  FNext.OnClick := NextClick;

  FNext.OnMouseDown := doButtonMouseDown;
  FNext.OnMouseUp   := doButtonMouseUp;
  FPrev.OnMouseDown := doButtonMouseDown;
  FPrev.OnMouseUp   := doButtonMouseUp;

  FPanelSpin := TRectangle.Create(FPanelBar);

{$IF CompilerVersion >= 27 }
  FPanelSpin.Stroke.Kind := TBrushKind.None;
{$ELSE}
  FPanelSpin.Stroke.Kind := TBrushKind.bkNone;
{$ENDIF}
  FPanelSpin.Parent := FPanelBar;
  FPanelSpin.position.x := FButtonSize * 3;

{$IF CompilerVersion >= 27 }
  FPanelSpin.Align := TAlignLayout.Client;
{$ELSE}
  FPanelSpin.Align := TAlignLayout.alClient;
{$ENDIF}
  FComboCount := TComboBox.Create(FPanelSpin);

  FComboCount.OnChange := doSpinEditChange;
  FComboCount.Parent := FPanelSpin;
{$IF CompilerVersion >= 27 }
  FComboCount.Align := TAlignLayout.Client;
{$ELSE}
  FComboCount.Align := TAlignLayout.alClient;
{$ENDIF}
  FImageSize := 256;

  FImage := TImage.Create(FMapillaryPanel);
  FImage.Parent := FMapillaryPanel;

{$IF CompilerVersion >= 27 }
  FImage.Align := TAlignLayout.Client;
{$ELSE}
  FImage.Align := TAlignLayout.alClient;
{$ENDIF}
  BuildButton(FClose, FImage, 28,
    'M697.4 759.2l61.8-61.8L573.8 512l185.4-185.4-61.8-61.8L512 450.2 326.6 264.8l-61.8 61.8L450.2 512 264.8 697.4l61.8 61.8L512 573.8z');

  FClose.Hint := 'Close';

  FClose.OnClick := doClose;
  FClose.Corners := [TCorner.BottomLeft, TCorner.BottomRight];

  FMapillaryLayer := TECMapillaryLayer.Create(Map);

  FMapillaryLayer.OnClick := doOnMapillaryLayerClick;
  FMapillaryLayer.OnEndRequest := doEndRequest;
  FMapillaryLayer.OnBeginRequest := doBeginRequest;

  FMapillaryLayer.LocalCache := Map.LocalCache;

  Add('Mapillary', FMapillaryPanel, ecTopRight);

  Component.OnChange := doOnChange;

  setXYRadius(5);
  setMargin(3);

  (*
    FSpinEdit.position.x := 2 * FMargin;
    FCount.position.x := 2 * FMargin + FSpinEdit.Width;
  *)

  visible := false;

end;

destructor TMapillaryComponent.Destroy;
begin
  inherited;
  FTimer.Free;
  FBtnTimer.Free;
  FSequences.Free;
  FBmp.Free;
  FMapillaryPanel.Free;
end;

end.
