// ------------------------------------------------------------------------------
// DPF.iOS.MKMapView Component
//
// Dadeh Pardazane Faragir ( DPF ) Co.
//
// Web: http://www.dpfaragir.com
//
// Developed By: Babak Yaghoobi
//
// Email #1: yaghoobi@dpfaragir.com
// Email #2: b_yaghobi@yahoo.com
//
// ------------------------------------------------------------------------------
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// ------------------------------------------------------------------------------
unit DPF.iOS.MKMapView;

interface

{$I DPF.iOS.Defs.inc}

uses
  System.SysUtils,
  System.Classes,
  System.Types,
  System.UITypes,
  System.Variants,
  System.Generics.Collections,
  System.TypInfo,
  System.Math,
  Data.DBXJSON,
  Data.DB,
  XML.xmldom,
  XML.XMLDoc,
  XML.XMLIntf,
  DPF.iOS.BaseControl,
{$IFDEF IOS}
  Macapi.ObjectiveC,
  Macapi.ObjCRuntime,
  IOSapi.CocoaTypes,
  IOSapi.Foundation,
  IOSapi.Uikit,
  IOSapi.CoreGraphics,
  IOSapi.CoreImage,
  IOSapi.CoreLocation,
  FMX.Platform.iOS,
  DPF.iOS.MapKit,
  DPF.iOS.Common,
{$ELSE}
  // Added by Fenistil
  DPF.iOS.DesignTime,
{$ENDIF}
{$IFDEF DELPHIXE5} FMX.Graphics, {$ENDIF}
  FMX.Types,
  FMX.Controls,
  FMX.Forms;

type

  TDPFMapKitMapType = ( mkmtStandard, mkmtSatellite, mkmtTypeHybrid );

  TDPFMapKitUserTrackMode = ( utmNone, utmFollow, utmFollowWithHeading );

{$IFDEF IOS}

  TGeoAddressInfo = record
    Location: CLLocationCoordinate2D;
  end;

  TDPFMapView = class;

  // ------------------------------------------------------------------------------
  TMKMapViewDelegate = class( TOCLocal, IMKMapViewDelegate )
  private
    FMapView: TDPFMapView;
  public
    constructor Create( aMapView: TDPFMapView );
    procedure mapView( mapView: MKMapView; didUpdateUserLocation: MKUserLocation ); overload; cdecl;
    function mapView( mapView: MKMapView; viewForAnnotation: MKAnnotation ): MKAnnotationView; overload; cdecl;
    function mapView( mapView: MKMapView; viewForOverlay: MKOverlay ): MKOverlayView; overload; cdecl;
    procedure mapView( mapView: MKMapView; regionDidChangeAnimated: Boolean ); overload; cdecl;

    procedure mapViewWillStartLoadingMap( mapView: MKMapView ); cdecl;
    procedure mapViewWillStartLocatingUser( mapView: MKMapView ); cdecl;
    procedure mapViewDidStopLocatingUser( mapView: MKMapView ); cdecl;
    procedure mapViewDidFinishLoadingMap( mapView: MKMapView ); cdecl;
    procedure mapViewDidFailLoadingMap( mapView: MKMapView; withError: NSError ); cdecl;
  end;
{$ENDIF}

  // ------------------------------------------------------------------------------
  [ComponentPlatformsAttribute( PidWin32 or PidWin64 or pidLinux32 or pidAndroid or PidOSX32 or PidiOSDevice or PidiOSSimulator )]
  TDPFMapView = class( TDPFiOSBaseControl )
  private
{$IFDEF IOS}
    FMKMapView        : MKMapView;
    FMKMapViewDelegate: TMKMapViewDelegate;
    FAnnotationsList  : TList<MKAnnotation>;
    FFromRouteLoc     : CLLocationCoordinate2D;
    FToRouteLoc       : CLLocationCoordinate2D;

{$ENDIF}
    FBackgroundColor : TAlphaColor;
    FShowUserLocation: Boolean;
    FMapType         : TDPFMapKitMapType;
    FZoomEnabled     : Boolean;
    FScrollEnabled   : Boolean;
    FUserTrackingMode: TDPFMapKitUserTrackMode;
    FZoomLevel       : Double;
    procedure SetBackgroundColor( const Value: TAlphaColor );
    procedure SetShowUserLocation( const Value: Boolean );
    procedure SetMapType( const Value: TDPFMapKitMapType );
    procedure SetZoomEnabled( const Value: Boolean );
    procedure SetScrollEnabled( const Value: Boolean );
    procedure SetUserTrackingMode( const Value: TDPFMapKitUserTrackMode );
    function GetUserLocationVisible: Boolean;
    procedure SetZoomLevel( const Value: Double );

  protected

    procedure Resize; override;
    procedure Move; override;
{$IFDEF IOS}
    procedure ParseJSONPairs( jo: TJSONObject );
    procedure ParseJSONArray( ja: TJSONArray );
    function XMLPars( XMLNode: IXMLNode ): TGeoAddressInfo;
    procedure DrawRoute( PolyStr: string );
    procedure DrawPolyLine( const Poly: TArray<CLLocationCoordinate2D> );
{$ELSE}
    procedure Paint; override;
{$ENDIF}
  public
{$IFDEF IOS}
    procedure Loaded; override;
{$ENDIF}
    constructor Create( AOwner: TComponent ); override;
    destructor Destroy; override;

{$IFDEF IOS}
    // Return Annotation ID
    function AddAnnotation( Title: string; SubTitle: string; LocationAddress: string; Zoom: Single = -1 ): MKPointAnnotation; overload;
    function AddAnnotation( Title: string; SubTitle: string; Latitude: CLLocationDegrees; longitude: CLLocationDegrees; Zoom: Single = -1 ): MKPointAnnotation; overload;
    procedure RemoveAnnotation( Annotation: MKPointAnnotation );
    procedure AddCircle( Latitude: CLLocationDegrees; longitude: CLLocationDegrees; radius: Double );
    function GetRoutePointFrom( FromPoint: CLLocationCoordinate2D; ToPoint: CLLocationCoordinate2D ): NSArray;
    function GetLatLngFromAddress( Addr: string ): CLLocationCoordinate2D;
    procedure ShowRoute( FromDir: string; ToDir: string );
{$ENDIF}
    procedure ZoomIn;
    procedure ZoomOut;
  published
    property BackgroundColor    : TAlphaColor read FBackgroundColor write SetBackgroundColor default TAlphaColors.Null;
    property ShowUserLocation   : Boolean read FShowUserLocation write SetShowUserLocation default True;
    property UserLocationVisible: Boolean read GetUserLocationVisible;
    property MapType            : TDPFMapKitMapType read FMapType write SetMapType default mkmtStandard;
    property ZoomEnabled        : Boolean read FZoomEnabled write SetZoomEnabled default True;
    property ScrollEnabled      : Boolean read FScrollEnabled write SetScrollEnabled default True;
    property UserTrackingMode   : TDPFMapKitUserTrackMode read FUserTrackingMode write SetUserTrackingMode default utmNone;
    property ZoomLevel          : Double read FZoomLevel write SetZoomLevel;

    property UserInteraction;
    property ContentMode;
    property Alpha;
    property Align;
    property Position;
    property Width;
    property Height;
    property Enabled;
    property Visible;
  end;

  // ------------------------------------------------------------------------------

implementation

// ------------------------------------------------------------------------------
{ TDPFMapView }
// ------------------------------------------------------------------------------
constructor TDPFMapView.Create( AOwner: TComponent );
begin
  inherited Create( AOwner );
  ControlCaption    := 'MapView';
  FZoomLevel        := 14;
  FVisible          := True;
  FBackgroundColor  := TAlphaColors.Null;
  FShowUserLocation := True;
  FMapType          := mkmtStandard;
  FZoomEnabled      := True;
  FScrollEnabled    := True;
  FUserTrackingMode := utmNone;
{$IFDEF IOS}
  FAnnotationsList   := TList<MKAnnotation>.Create;
  FMKMapViewDelegate := TMKMapViewDelegate.Create( Self );
  FMKMapView         := TMKMapView.Wrap( TMKMapView.alloc.initWithFrame( CGRectMake( Position.X, Position.Y, Width, Height ) ) );
  FUIControl         := FMKMapView;

{$ENDIF}
end;

// ------------------------------------------------------------------------------
destructor TDPFMapView.Destroy;
begin
{$IFDEF IOS}
  if FMKMapViewDelegate <> nil then
    FMKMapViewDelegate.DisposeOf;
  if FAnnotationsList <> nil then
    FAnnotationsList.DisposeOf;
{$ENDIF}
  inherited;
end;

{$IFDEF IOS}

// ------------------------------------------------------------------------------
procedure TDPFMapView.AddCircle( Latitude, longitude: CLLocationDegrees; radius: Double );
var
  Circle    : MKCircle;
  Coordinate: CLLocationCoordinate2D;
begin
{$IFDEF IOS}
  if FMKMapView <> nil then
  begin
    Coordinate.Latitude  := Latitude;
    Coordinate.longitude := longitude;
    Circle               := TMKCircle.Wrap( TMKCircle.OCClass.circleWithCenterCoordinate( Coordinate, radius ) );
    FMKMapView.addOverlay( Circle );
  end;
{$ENDIF}
end;

// ------------------------------------------------------------------------------
function TDPFMapView.AddAnnotation( Title: string; SubTitle: string; LocationAddress: string; Zoom: Single ): MKPointAnnotation;
var
  GeoAddr: TGeoAddressInfo;
begin
  GeoAddr.Location := GetLatLngFromAddress( LocationAddress );
  result           := AddAnnotation( Title, SubTitle, GeoAddr.Location.latitude, GeoAddr.Location.longitude, Zoom );
end;

// ------------------------------------------------------------------------------
function TDPFMapView.AddAnnotation( Title: string; SubTitle: string; Latitude: CLLocationDegrees; longitude: CLLocationDegrees; Zoom: Single = -1 ): MKPointAnnotation;
var
  Coordinate: CLLocationCoordinate2D;
begin
  if FMKMapView <> nil then
  begin
    Result               := TMKPointAnnotation.Create;
    Coordinate.Latitude  := Latitude;
    Coordinate.longitude := longitude;
    Result.setTitle( NSStr( Title ) );
    Result.setSubTitle( NSStr( SubTitle ) );
    Result.setCoordinate( Coordinate );
    FMKMapView.AddAnnotation( Result );

    FMKMapView.setCenterCoordinate( Result.Coordinate, True );
    if Zoom <> -1 then
      setCenterCoordinate( FMKMapView, Result.Coordinate, Zoom, True );
    FAnnotationsList.Add( Result );
  end;
end;

// ------------------------------------------------------------------------------
procedure TDPFMapView.RemoveAnnotation( Annotation: MKPointAnnotation );
begin
  if FMKMapView <> nil then
  begin
    if FAnnotationsList.Contains( Annotation ) then
    begin
      FMKMapView.RemoveAnnotation( Annotation );
      FAnnotationsList.Remove( Annotation );
    end;
  end;
end;
{$ENDIF}

// ------------------------------------------------------------------------------
function TDPFMapView.GetUserLocationVisible: Boolean;
begin
  Result := False;
{$IFDEF IOS}
  if FMKMapView <> nil then
  begin
    Result := FMKMapView.UserLocationVisible;
  end;
{$ENDIF}
end;

// ------------------------------------------------------------------------------
{$IFDEF IOS}

procedure TDPFMapView.Loaded;
begin
  FMKMapView.SetFrame( CGRectMake( Position.X, Position.Y, Width, Height ) );
  FMKMapView.setHidden( not Visible );
  FMKMapView.setShowsUserLocation( FShowUserLocation );

  FMKMapView.SetMapType( Integer( FMapType ) );
  FMKMapView.SetZoomEnabled( FZoomEnabled );
  FMKMapView.SetScrollEnabled( FScrollEnabled );
  FMKMapView.SetUserTrackingMode( Integer( FUserTrackingMode ), True );

  if FZoomLevel = 0 then
    FZoomLevel := DPF.iOS.MapKit.GetZoomLevel( FMKMapView );

  if FBackgroundColor = TAlphaColors.Null then
    FMKMapView.SetBackgroundColor( TUIColor.Wrap( TUIColor.OCClass.clearColor ) )
  else
    FMKMapView.SetBackgroundColor( TColorToUIColor( FBackgroundColor ) );

  FMKMapView.setDelegate( FMKMapViewDelegate.GetObjectID );

  AddSubView( Self, ParentControl );
  // ----------------------------
  // Important
  inherited;
end;
{$ENDIF}

// ------------------------------------------------------------------------------
procedure TDPFMapView.Resize;
begin
  inherited;
{$IFDEF IOS}
  if FMKMapView <> nil then
  begin
    FMKMapView.SetFrame( CGRectMake( Position.X, Position.Y, Width, Height ) );
  end;
{$ENDIF}
end;

// ------------------------------------------------------------------------------
procedure TDPFMapView.SetBackgroundColor( const Value: TAlphaColor );
begin
  FBackgroundColor := Value;
{$IFDEF IOS}
  if FMKMapView <> nil then
  begin
    if FBackgroundColor <> TAlphaColors.Null then
      FMKMapView.SetBackgroundColor( TColorToUIColor( FBackgroundColor ) )
    else
      FMKMapView.SetBackgroundColor( TUIColor.Wrap( TUIColor.OCClass.clearColor ) );
  end;
{$ENDIF}
end;

// ------------------------------------------------------------------------------
procedure TDPFMapView.SetMapType( const Value: TDPFMapKitMapType );
begin
  FMapType := Value;
{$IFDEF IOS}
  if FMKMapView <> nil then
  begin
    FMKMapView.SetMapType( Integer( FMapType ) );
  end;
{$ENDIF}
end;

// ------------------------------------------------------------------------------
procedure TDPFMapView.SetScrollEnabled( const Value: Boolean );
begin
  FScrollEnabled := Value;
{$IFDEF IOS}
  if FMKMapView <> nil then
  begin
    FMKMapView.SetScrollEnabled( FScrollEnabled )
  end;
{$ENDIF}
end;

// ------------------------------------------------------------------------------
procedure TDPFMapView.SetShowUserLocation( const Value: Boolean );
begin
  FShowUserLocation := Value;
{$IFDEF IOS}
  if FMKMapView <> nil then
  begin
    FMKMapView.setShowsUserLocation( FShowUserLocation )
  end;
{$ENDIF}
end;

// ------------------------------------------------------------------------------
procedure TDPFMapView.SetUserTrackingMode( const Value: TDPFMapKitUserTrackMode );
begin
  FUserTrackingMode := Value;
{$IFDEF IOS}
  if FMKMapView <> nil then
    FMKMapView.SetUserTrackingMode( Integer( FUserTrackingMode ), True );
{$ENDIF}
end;

// ------------------------------------------------------------------------------
procedure TDPFMapView.SetZoomEnabled( const Value: Boolean );
begin
  FZoomEnabled := Value;
{$IFDEF IOS}
  if FMKMapView <> nil then
    FMKMapView.SetZoomEnabled( FZoomEnabled );
{$ENDIF}
end;

// ------------------------------------------------------------------------------
procedure TDPFMapView.SetZoomLevel( const Value: Double );
begin
  if ( Value < 2 ) or ( Value > 20 ) then
    exit;

  FZoomLevel := Value;
{$IFDEF IOS}
  if FMKMapView <> nil then
    setCenterCoordinate( FMKMapView, FMKMapView.centerCoordinate, Value, True );
{$ENDIF}
end;

// ------------------------------------------------------------------------------
procedure TDPFMapView.ZoomIn;
begin
{$IFDEF IOS}
  if FMKMapView <> nil then
  begin
    setCenterCoordinate( FMKMapView, FMKMapView.region.center, 10, True );
  end;
{$ENDIF}
end;

// ------------------------------------------------------------------------------
procedure TDPFMapView.ZoomOut;
{$IFDEF IOS}
var
  newRegion: MKCoordinateRegion;
{$ENDIF}
begin
{$IFDEF IOS}
  if FMKMapView <> nil then
  begin
    newRegion.center              := FMKMapView.region.center;
    newRegion.span.latitudeDelta  := Min( FMKMapView.region.span.latitudeDelta / 0.8, 180.0 );
    newRegion.span.longitudeDelta := Min( FMKMapView.region.span.longitudeDelta / 0.8, 180.0 );
    FMKMapView.setRegion( newRegion, True );
    FMKMapView.regionThatFits( newRegion );
  end;
{$ENDIF}
end;

// ------------------------------------------------------------------------------
procedure TDPFMapView.Move;
begin
  Resize;
end;

{$IFNDEF IOS}

// ------------------------------------------------------------------------------
procedure TDPFMapView.Paint;
var
  x, r: integer;
  Rect: TRectF;
begin
  // Added by Fenistil
  Canvas.BeginScene;
  Canvas.Fill.Color := $FFF0EBD3;
  Canvas.Fill.Kind  := TBrushKind.bkSolid;
  Canvas.FillRect( ClipRect, 0, 0, AllCorners, 1, TCornerType.ctRound );
  r := 50;
  if Min( Width, Height ) < 50 then
    r                     := Round( Max( 5, Min( Width, Height ) / 3 ) );
  Canvas.Stroke.Color     := $FFE0D9BC;
  Canvas.Stroke.Cap       := TStrokeCap.scRound;
  Canvas.Stroke.Join      := TStrokeJoin.sjRound;
  Canvas.Stroke.Dash      := TStrokeDash.sdSolid;
  Canvas.Stroke.Thickness := 1;
  // Vertical Lines
  x := 10;
  while x < Width do
  begin
    Canvas.DrawLine( PointF( x, 0 ), PointF( x, Height ), 1 );
    inc( x, r );
  end;
  // Horizontal Lines
  x := 10;
  while x < Height do
  begin
    Canvas.DrawLine( PointF( 0, x ), PointF( Width, x ), 1 );
    inc( x, r );
  end;
  // TrackingDot
  BitmapToPosition( Self, iOS_GUI_Bitmaps.MapView.TrackingDot, Width / 2 - iOS_GUI_Bitmaps.MapView.TrackingDot.Width / 2, Height / 2 - iOS_GUI_Bitmaps.MapView.TrackingDot.Height / 2 );
  BitmapToPosition( Self, iOS_GUI_Bitmaps.MapView.TrackingDotHalo, Width / 2 - iOS_GUI_Bitmaps.MapView.TrackingDotHalo.Width / 2, Height / 2 - iOS_GUI_Bitmaps.MapView.TrackingDotHalo.Height / 2 );
  // Component Name
  Rect              := ClipRect;
  Rect.Bottom       := Rect.Bottom / 2;
  Canvas.Font.Size  := 14;
  Canvas.Fill.Color := TAlphaColors.Black;
  Canvas.FillText( Rect, name, True, 1, [], TTextAlign.taCenter, TTextAlign.taCenter );
  Canvas.EndScene;
end;
{$ENDIF}
{$IFDEF IOS}

// ------------------------------------------------------------------------------
function TDPFMapView.XMLPars( XMLNode: IXMLNode ): TGeoAddressInfo;
var
  i: integer;
  // lat, lng: string;
  S: string;
begin
  if ( XMLNode.NodeType = ntText ) then
  begin
    S := XMLNode.NodeValue;
  end
  else
  begin
    if sameText( XMLNode.NodeName, 'geometry' ) then
    begin
      S := XMLNode.ChildNodes.Nodes[0].NodeName;
      S := XMLNode.ChildNodes.Nodes[0].ChildNodes.Nodes[0].NodeName;

      Result.Location.latitude  := XMLNode.ChildNodes.Nodes[0].ChildNodes.Nodes[0].NodeValue;
      Result.Location.longitude := XMLNode.ChildNodes.Nodes[0].ChildNodes.Nodes[1].NodeValue;
      exit;
    end;
  end;
  for i := 0 to XMLNode.ChildNodes.Count - 1 do
  begin
    if Result.Location.longitude <> 0 then
      exit;
    Result := XMLPars( XMLNode.ChildNodes.Nodes[i] );
    if Result.Location.longitude <> 0 then
      exit;
  end;
end;

// ------------------------------------------------------------------------------
procedure TDPFMapView.ParseJSONArray( ja: TJSONArray );
var
  i: Integer;
  S: string;
begin
  for i := 0 to ja.Size - 1 do
  begin
    if ja.Get( i ) is TJSONArray then
    begin
      ParseJSONArray( ja.Get( i ) as TJSONArray );
    end
    else if ja.Get( i ) is TJSONObject then
    begin
      ParseJSONPairs( ja.Get( i ) as TJSONObject );
    end
    else
    begin
      S := ja.Get( i ).Value;
      if SameText( S, 'points' ) then
        DrawRoute( S );
    end
  end;
end;

// ------------------------------------------------------------------------------
procedure TDPFMapView.ParseJSONPairs( jo: TJSONObject );
var
  i    : Integer;
  jv   : TJSONValue;
  S, S2: string;
begin
  for i := 0 to jo.Size - 1 do
  begin
    jv := jo.Get( i ).JsonValue;
    S2 := jo.Get( i ).JsonString.Value;
    if sametext( S2, 'overview_polyline' ) then
    begin
      S2 := jo.Get( i ).JsonString.Value;
      Continue;
    end;

    if jv is TJSONOBject then
    begin
      ParseJSONPairs( jv as TJSONObject );
    end
    else if jv is TJSONArray then
    begin
      ParseJSONArray( jv as TJSONArray );
    end
    else
    begin
      S := jo.Get( i ).JsonString.Value;
      if SameText( S, 'points' ) then
      begin
        S := jo.Get( i ).JsonValue.Value;
        DrawRoute( S );
      end;
    end
  end;
end;

// ------------------------------------------------------------------------------
procedure TDPFMapView.DrawPolyLine( const Poly: TArray<CLLocationCoordinate2D> );
var
  polyLine: MKPolyline;
begin
  polyLine := TMKPolyline.OCClass.polylineWithCoordinates( @Poly[0], Length( Poly ) );
  FMKMapView.addOverlay( polyLine );
  FMKMapView.setVisibleMapRect( polyLine.boundingMapRect );

  if FFromRouteLoc.latitude = 1000 then
  begin
    FFromRouteLoc.latitude  := Poly[0].latitude;
    FFromRouteLoc.longitude := Poly[0].longitude;
  end
  else
  begin
    FToRouteLoc.latitude  := Poly[high( Poly )].latitude;
    FToRouteLoc.longitude := Poly[high( Poly )].longitude;
  end
end;

// ------------------------------------------------------------------------------
procedure TDPFMapView.DrawRoute( PolyStr: string );
var
  Poly: TArray<CLLocationCoordinate2D>;
begin

  Poly := PolyDecode( PolyStr );
  DrawPolyLine( Poly );
end;

// ------------------------------------------------------------------------------
procedure TDPFMapView.ShowRoute( FromDir: string; ToDir: string );
var
  escaped_address1, escaped_address2: NSString;
  requestString                     : string;
  url                               : NSURL;
  _result                           : NSString;
  jo                                : TJSONObject;
  Data                              : string;
begin

  escaped_address1 := NSStr( FromDir ).stringByAddingPercentEscapesUsingEncoding( NSUTF8StringEncoding );
  escaped_address2 := NSStr( ToDir ).stringByAddingPercentEscapesUsingEncoding( NSUTF8StringEncoding );
  requestString    := Format( 'http://maps.googleapis.com/maps/api/directions/json?origin=%s&destination=%s&sensor=false&dirflg=w&mode=driving', [escaped_address1.UTF8String, escaped_address2.UTF8String] );
  url              := TNSURL.Wrap( TNSURL.OCClass.URLWithString( NSStr( requestString ) ) );
  _result          := TNSString.Wrap( TNSString.OCClass.stringWithContentsOfURL( url, NSUTF8StringEncoding, nil ) );

  Data := UTF8ToString( _result.UTF8String );
  if Data = '' then
    exit;

  FFromRouteLoc.latitude  := 1000;
  FFromRouteLoc.longitude := 1000;
  FToRouteLoc.latitude    := 1000;
  FToRouteLoc.longitude   := 1000;

  jo := TJSONObject.ParseJSONValue( TEncoding.UTF8.GetBytes( Data ), 0, true ) as TJSONObject;
  ParseJSONPairs( jo );

  if FFromRouteLoc.latitude <> 1000 then
    AddAnnotation( FromDir, '', FFromRouteLoc.latitude, FFromRouteLoc.longitude );

  if FToRouteLoc.latitude <> 1000 then
    AddAnnotation( ToDir, '', FToRouteLoc.latitude, FToRouteLoc.longitude );

end;

// ------------------------------------------------------------------------------
function TDPFMapView.GetLatLngFromAddress( Addr: string ): CLLocationCoordinate2D;
var
  escaped_address: NSString;
  requestString  : string;
  url            : NSURL;
  _result        : NSString;
  Doc            : IXMLDocument;
begin

  escaped_address := NSStr( Addr ).stringByAddingPercentEscapesUsingEncoding( NSUTF8StringEncoding );
  requestString   := Format( 'https://maps.googleapis.com/maps/api/geocode/xml?address=%s&sensor=false', [escaped_address.UTF8String] );
  url             := TNSURL.Wrap( TNSURL.OCClass.URLWithString( NSStr( requestString ) ) );
  _result         := TNSString.Wrap( TNSString.OCClass.stringWithContentsOfURL( url, NSUTF8StringEncoding, nil ) );

  Doc    := LoadXMLData( UTF8ToString( _result.UTF8String ) );
  Result := XMLPars( Doc.DocumentElement ).Location;

end;

// ------------------------------------------------------------------------------
function TDPFMapView.GetRoutePointFrom( FromPoint: CLLocationCoordinate2D; ToPoint: CLLocationCoordinate2D ): NSArray;
var
  // saddr        : NSString;
  // daddr        : NSString;
  // apiUrlStr    : NSString;
  S1, S2     : string;
  apiUrl     : NSURL;
  error      : {$IFDEF DELPHIXE5}PPointer{$ELSE}NSError{$ENDIF};
  apiResponse: NSString;
  // encodedPoints: TNSString;
begin

  S1 := Format( '%f,%f', [FromPoint.latitude, FromPoint.longitude] );
  S2 := Format( '%f,%f', [ToPoint.latitude, ToPoint.longitude] );

  S1 := Format( 'http://maps.google.com/?addr=%s&daddr=%s', [S1, S2] );

  apiUrl := TNSURL.Wrap( TNSURL.OCClass.URLWithString( NSStr( S1 ) ) );

  error       := nil;
  apiResponse := TNSString.Wrap( TNSString.OCClass.stringWithContentsOfURL( apiUrl, NSUTF8StringEncoding, error ) );
  // encodedPoints:= TNSString.Wrap( apiResponse ).stringByMatching:@"points:\\\"([^\\\"]*)\\\"" capture:1L];

  // return [self decodePolyLine:[encodedPoints mutableCopy]];
end;

// ----------------------------------------------------------------------------
{ TMKMapViewDelegate }
constructor TMKMapViewDelegate.Create( aMapView: TDPFMapView );
begin
  inherited Create;
  FMapView := aMapView;
end;

// ----------------------------------------------------------------------------
procedure TMKMapViewDelegate.mapView( mapView: MKMapView; didUpdateUserLocation: MKUserLocation );
begin

end;

// ----------------------------------------------------------------------------
function TMKMapViewDelegate.mapView( mapView: MKMapView; viewForAnnotation: MKAnnotation ): MKAnnotationView;
{ const
  AnnotationIdent = 'Annotation';
  var
  pinView: MKPinAnnotationView;
  P      : Pointer;
  an     : MKAnnotation; }
begin
  result := nil;
  (* Complete in the next version

    P := mapView.dequeueReusableAnnotationViewWithIdentifier( NSStr( AnnotationIdent ) );
    If Not Assigned( P ) Then
    Begin
    pinView := TMKPinAnnotationView.Wrap( TMKPinAnnotationView.alloc.initWithAnnotation( viewForAnnotation, NSStr( AnnotationIdent ) ) );
    If FMapView.FAnnotationsList.Contains( pinView.annotation ) Then
    Begin
    pinView.setPinColor( MKPinAnnotationColorPurple );
    pinView.setDraggable( true );
    pinView.setAnimatesDrop( True );
    pinView.setCanShowCallout( True ); // Very Important For Title and SubTitle Showing !!!
    End;
    End
    Else
    Begin
    pinView := TMKPinAnnotationView.Wrap( P );
    End;
    result := pinView;
  *)
end;

// ----------------------------------------------------------------------------
procedure TMKMapViewDelegate.mapViewDidFailLoadingMap( mapView: MKMapView; withError: NSError );
begin
  { }
end;

// ----------------------------------------------------------------------------
procedure TMKMapViewDelegate.mapViewDidFinishLoadingMap( mapView: MKMapView );
begin
  { }
end;

// ----------------------------------------------------------------------------
procedure TMKMapViewDelegate.mapViewDidStopLocatingUser( mapView: MKMapView );
begin
  { }
end;

// ----------------------------------------------------------------------------
procedure TMKMapViewDelegate.mapViewWillStartLoadingMap( mapView: MKMapView );
begin
  { }
end;

// ----------------------------------------------------------------------------
procedure TMKMapViewDelegate.mapViewWillStartLocatingUser( mapView: MKMapView );
// var ALocation: CLLocationCoordinate2D;
begin
  { if FMapView.FShowUserLocation then
    begin
    ALocation := FMapView.FMKMapView.userLocation.location.coordinate;
    FMapView.FMKMapView.setCenterCoordinate( ALocation, True );
    end; }
end;

// ----------------------------------------------------------------------------
procedure TMKMapViewDelegate.mapView( mapView: MKMapView; regionDidChangeAnimated: Boolean );
begin
  { }
end;

// ----------------------------------------------------------------------------
function TMKMapViewDelegate.mapView( mapView: MKMapView; viewForOverlay: MKOverlay ): MKOverlayView;
var
  PolyLineView: MKPolylineView;
begin
  if viewForOverlay.isKindOfClass( objc_getClass( 'MKPolyline' ) ) then
  begin
    PolyLineView := TMKPolylineView.Wrap( TMKPolylineView.Alloc.initWithPolyline( MKPolyline( viewForOverlay ) ) );
    PolyLineView.setFillColor( TColorToUIColor( TAlphaColors.Red ) );
    PolyLineView.setStrokeColor( TColorToUIColor( TAlphaColors.Green ) );
    PolyLineView.setLineWidth( 3 );
    result := PolyLineView;
  end;

end;

{$ENDIF}

// ----------------------------------------------------------------------------
end.
