Composant Delphi / Google Maps / OpenStreetMap / Leaflet  / Mappilary / Native Maps 100% Delphi 0% WebBrowser 0% Javascript


you are here :TECMap > Overlays

TECNativeLayer is the base class of the layers that allow you to react to the displayed area, and mouse actions.

TECNativeLayer = class
FObserver : TNativeMapObserver;
FShapes : TECShapes;
FMap : TNativeMapControl;

FOnShapeClick : TOnShapeMouseEvent;

FOnMouseMove : TNotifyEvent;

function getMinZoom : byte;
function getMaxZoom : byte;

procedure setMinZoom(value:byte);
procedure setMaxZoom(value:byte);

function getVisible : boolean;


procedure doOnMapEndMove(sender : TObject); virtual;
procedure doOnShapeClick(sender : TObject); virtual;
procedure doOnShapeRightClick(sender : TObject); virtual;
procedure doOnMapMouseMove(sender : TObject); virtual;
procedure doOnMapMouseClick(sender : TObject); virtual;
procedure doOnMapHiResChange(sender : TObject); virtual;


procedure setVisible(const value:boolean); virtual;

constructor Create(_FMap:TnativeMapControl;const Name:string); virtual;
destructor Destroy; override;

property OnShapeClick : TOnShapeMouseEvent read FOnShapeClick write FOnShapeClick;
property OnShapeRightClick : TOnShapeMouseEvent read FOnShaperightClick write FOnShapeRightClick;

property OnMouseMove : TNotifyEvent read FOnMouseMove write FOnMouseMove;
property OnMouseClick : TNotifyEvent read FOnMouseClick write FOnMouseClick;

property Map : TNativeMapControl read FMap;
property Shapes : TECShapes read FShapes;
property Visible : boolean read getVisible write setVisible;

property MaxZoom : byte read getMaxZoom write setMaxZoom;
property MinZoom : byte read getMinZoom write setMinZoom;


You can either plug into OnMapHiResChange or overload the doOnMapHiResChange(sender: TObject) procedure so that your layer adapts to change resolution



Google closed Panoramio November 4, 2016, the service is no longer functional


A Panoramio layer is integrated into TECNativeMap through the property PanoramioLayer

// show panoramio layer
map.PanoramioLayer := true;

The event OnPanoramioClick(sender: TObject; const Item: TECShape; const ownerId, ownerName, PhotoId, PhotoDate, PhotoTitle, PhotoUrl, copyright: string) is raised when clicking a Panoramio item.

Fig. 89 Click sur un élément Panoramio


Allows you to automatically display the result of a search

To use this layer you must embed unit uecNativePlaceLayer or FMX.uecNativePlaceLayer depending on whether you use the VCL version or FireMonkey


Example: a layer that displays restaurants

// search Layer

FPlacesLayer := TECNativePlaceLayer.create(map,'PLACES_LAYER');
FPlacesLayer.OnPLaceClick := doOnPlaceClick;

FPlacesLayer.Visible := true;
FPlacesLayer.Search := 'node[amenity=restaurant]';
// standard image 32x32
FPlacesLayer.MarkerFilename := '';
// lat,lng on center of image
FPlacesLayer.XAnchor := 16;
FPlacesLayer.YAnchor := 16;
// hi-res image 64*64
FPlacesLayer.MarkerHiResFilename := '';
// lat,lng on center of image
FPlacesLayer.HiResXAnchor := 32;
FPlacesLayer.HiResYAnchor := 32;

// event click on place shape
procedure TForm1.doOnPlaceClick(sender : TECShape);
pResult : TECPlaceResult;
Content : string;
i : integer;

// here Sender and Sender.Item are allway assigned, and Sender.Item is TECPlaceResult

pResult :=TECPlaceResult(Sender.item)

for i:=0 to pResult.CountResult-1 do

n := pResult.NameResult[i];
r := pResult.Result[n];
if length(n)< 9 then
n := n+'<tab="65">';

content := content+'<b>'+n+'</b>: '+r+'<br>';


if FPlacesLayer.Shapes.InfoWindows.Count=0 then
FPlacesLayer.Shapes.InfoWindows[0].zindex := 100;

FPlacesLayer.Shapes.InfoWindows[0].content := Content;
FPlacesLayer.Shapes.InfoWindows[0].Visible := true;



Fig. 90 Click on an element TECNativePlaceLayer

By default the elements of layer are TECShapeMarker but you can change the type of TECShape by redefining TECNativePlaceLayer.doCreateShape(SearchResult : TECPlaceResult):TECShape;


XapiLayer uses TECNativePlaceLayer and is directly integrated with TECNativeMap.

The search is performed in the area displayed by your map, it is updated on each move

property Shapes:TECShapes

The group that contains the items, it's name is 'XAPI'

property Search : string

Research is done by doing a query on a server XAPI

You can find all the tags OSM using a syntax like 'node[key=value]'

// search bus stop
map.XapiLayer.Search := 'node[highway=bus_stop]';

For nodes you can use simplified syntax.

// search bus stop
map.XapiLayer.Search := 'highway=bus_stop';

For the key amenity you can simplify even more.

// search all restaurant, bar and café
map.XapiLayer.Search := 'restaurant|bar|cafe';

property Visible : boolean

show / hide layer

property MaxItem : integer

Maximum number of items

property OnClick : TOnShape

This event is raised when an item is clicked

property OnChange : TNotifyEvent

Raised after each request
map.XapiLayer.OnClick := doOnXapiClick;
map.XapiLayer.OnChange := doOnXapiChange;
procedure TForm1.doOnXapiChange(sender : TObject);

// event click on xapi shape
procedure TForm1.doOnXapiClick(sender : TECshape);

Use the styles to decorate your items

Selector := '#'+map.XapiLayer.Shapes.Name+ '.marker.amenity';

map.styles.addRule(Selector+':restaurant' +
+ 'JYQIDOMg0NDYawBIFC2FgCSCxBerFMg0Es02AAP34wMx8/aIAAAAAASUVORK5CYII=;visible:true;');

map.styles.addRule(Selector+':bar' +
+ 'iFmCxWaDLMaEAsoqDQBNGsGlDeHyexnbJPAHwpKuIe9zSwDFzFPR6egM4P9HMgX3YQgDSr67gQrsq51'
+ 'z8ZqqqSwHrOkhsZqguAo0iyS5weL1kD5qZUcjPNfXV1HPIThmaY9vr4QH8KknbPXbfMgtiqvSMA+1Zy'
+ 'lrNy9/SK8g0PVamc2NlTK98F1MyKB+QkmGEAAAAASUVORK5CYII=;visible:true;}');

map.styles.addRule(Selector+':cafe' +
+ 'JUBsgE2eEotQDIHyHXDJU2JRPyn8wQmgcfCfTPye1HghGw8/i4D4PBrbgVYWYcMGtLAoAakUmQ8VW0/'
+ 'RGS2+aW0SzGhYAB5lDXBZ7NtoAAAAASUVORK5CYII=;visible:true;}');

map.styles.addRule('#'+map.XapiLayer.Shapes.Name+ '.marker.highway:bus_stop {color:blue;styleicon:Flat;visible:true;}');
map.styles.addRule('#'+map.XapiLayer.Shapes.Name+ '.marker {scale:1;}');
map.styles.addRule('#'+map.XapiLayer.Shapes.Name+ '.marker:hover {scale:1.5;}');

Fig. 91 'restaurant|bar|cafe'

Fig. 92 'highway=bus_stop'


With way[key = value] you can retrieve paths.

// find roads
map.XapiLayer.Search := 'way[highway=motorway|primary|secondary|tertiary|residential]';
// styles routes
map.styles.addRule('#' + map.XapiLayer.Shapes.Name +
'.line.highway:primary {zindex:9;weight:4;color:gradient(Red,Yellow,0.3);visible:true;}');
map.styles.addRule('#' + map.XapiLayer.Shapes.Name +
'.line.highway:secondary {zindex:8;weight:3;color:gradient(Red,Yellow,0.4);visible:true;}');

Fig. 93 Xapi Way

You can also find the junctions between routes.

map.XapiLayer.Junction := true;
map.XapiLayer.Search := 'way[highway=unclassified|road|motorway|trunk|primary|secondary|tertiary|residential]';

Fig. 94 Xapi Roads & Junction

Manual search

When layer is visible the search is synchronized with the visible area of your map.

To perform a search on any area you need to hide layer and use Bound to delimit the search box.

// sample, copie data in another group

map.XapiLayer.OnChange := XapiChange;
map.XapiLayer.visible := false; := 'highway=bus_stop';
// ! call bound after set search !
TForm.XapiChange(sender : TObject);
// here xapilayer contain openstreetmap data
// copie in another group[copy_xapi'].ToTxt := map.XapiLayer.shapes.ToTxt;


Only available for Delphi 10.x and higher!


Mapillary is a service of the same type as Google Street Map, it allows you to display photos at street level, the advantage is that you can enrich it yourself by recording your routes.

TECNativeMap only uses open source information from Mapillary, you need a key to use Mapillary services.

For this go to the site, connect, add your applications, you will then get a token which will serve as your access key.

To use mapillary you must incorporate the unit uecMappilary or FMX.uecMappilarydepending on whether you are using the VCL or FireMonkey


Use TECMapillaryLayer to display a Mapillary layer.

FMappilaryLayer := TECMappilaryLayer.Create(map);
FMappilaryLayer.AccessToken := 'HERE-YOUR-TOKEN';

FMappilaryLayer.Visible := true;

Fig. 95 Demo Mapillary


function FindImageClose(const Lat, Lng: double; Distance: integer; var seq : TMapillarySequence; var PhotoIndex : integer ) : integer;

Find the nearest photo among those displayed in the layer, the search is local among the elements displayed on the map.

Lat,Lng indicates the starting point of the search

Distance indicates the maximum distance in meters from the search area

Seq will contain the sequence found or nil

PhotoIndex will contain the index of the picture in seq or -1

Returns the distance in meters between the search point and the photo, 0 if no photo

function SearchImageClose(const Lat,Lng:double; DistanceMeter : integer; ListSequenceImage : TListSequenceImage):integer;

Search all the images included in a radius of DistanceMeter meters, the search is in local

Lat,Lng indicates the starting point of the search

DistanceMeter indicates the maximum distance in meters of the search radius

ListSequenceImage will contain a list of TSequenceImage (a couple sequence, image index)

Returns the number of images found

See the demo MappilarySearchImages for a usage example


Fig. 96 DemoFiremonkeyMappilarySearchImages

procedure SearchBounds(NorthEastLatitude, NorthEastLongitude, SouthWestLatitude, SouthWestLongitude: double);

Run a manual search on the Mapillary servers to find the images of the area
property MinZoom : byte
Minimal zooming so that Mapillary queries are executed automatically depending on the visible area of your map, default zoom 15
function LoadMapillaryBitmap(const url: string; const FBitmap: TBitmap): boolean;
Load FBitmap with the url of an image

function DetectionsImage(const image_id:int64;const List:TListDataDetections):integer;

TDataDetections = record
geometry : string;

Fill the list with all the items detected in the image, returns the number of items

// search for elements contained in the image
L := TListDataDetections.Create;
n := FMapillaryLayer.DetectionsImage(SelectedSequence[SelectedImageIndex].Id,L);

for i := 0 to n-1 do


property AccessToken: string ;

MANDATORY your key to use Mapillary services
property LocalCache: string ;
Local cache directory, the one on your map is used when creating

property Tiles: TMapillaryTiles

List of TMapillaryTile currently in memory

a TMapillaryTile contains a property Sequences : TMapillarySequences which is a list of TMapillarySequence which itself is a list of TMapillaryImage

TMapillaryImage = class
property Compass_angle: int64 ;

property Lat: double ;
property Lng: double ;

property Sequence_id: string ;
property Organization_id: int64 ;
property Id: int64 ;

property Captured_at: TDateTime ;

property is_Pano: boolean ;

property Url256: string;
property Url1024: string;
property Url2048: string;

there is also a property TrafficSign:TListTrafficSign which is a list of TTrafficSign

TTrafficSign = record
id : int64;
last_seen_at : TDateTime;
lat,lng : double;
value: string;

procedure Clear;

Erase all Mapillary elements from your map

The data contained in the Tiles are not erased, so there will be no need to reconnect to the mapillary servers to display them again


procedure ClearAll;

Delete displayed items and data from Tiles

property Visible : boolean

Shows or hides the Mapillary layer

Visible acts on both images and signage


property TrafficSignVisible : boolean

Displays or hides signage

property MaxDayInCache : integer;
Lifetime of the data stored in the local cache, by default 30 days
property OnClick: TOnMapillaryLayerClick ;

Triggered when clicking on a mapillary image

// click on line or marker mapillary
procedure TForm.doOnMapillaryLayerClick(Layer : TECMapillaryLayer; item : TECShape;
MapillarySequence : TMapillarySequence; ImageIndex : integer);
var bmp:TBitmap;
// show photo in a TImage
bmp := TBitmap.Create;
// load image 256*256
// also url1024 and url2048

if FMapillaryLayer.LoadMapillaryBitmap(MapillarySequence[ImageIndex].Url256,bmp) then


property OnTrafficSignClick: TOnMapillaryLayerTrafficSignClick ;

Triggered when clicking on a traffic mapillary element

// click on marker mapillary traffic sign
procedure TForm30.doOnMapillaryLayerTrafficSignClick(layer: TECMapillaryLayer; item: TECShape;
ListTrafficSign: TListTrafficSign; TrafficSignIndex: integer) ;
'<h4>'+ListTrafficSign[TrafficSignIndex].value+ '</h4><br>'+
'<tab="10"><b>Id</b><tab="40"> : '+IntToStr(ListTrafficSign[TrafficSignIndex].id)+ '<br>'+
'<tab="10"><b>Lat</b><tab="40"> : '+DoubleToStrDigit(ListTrafficSign[TrafficSignIndex].lat, 5)+'<br>'+
'<tab="10"><b>Lng</b><tab="40"> : '+DoubleToStrDigit(ListTrafficSign[TrafficSignIndex].lng, 5),
250 // width=250
property OnSequenceColor:TOnMapillaySequenceColor;

Allows you to assign a color to a sequence.

By default a unique color is assigned according to the sequence id, so a sequence will always have the same color.

Layer.OnSequenceColor := doSequenceColor;

procedure TForm2.doSequenceColor(Layer : TECMapillaryLayer;
MapillarySequence: TMapillarySequence;
var SequenceColor:TColor);
SequenceColor := your_color;

property OnBeginRequest: TNotifyEvent

Event triggered just before launching a Mapillary request

property OnEndRequest: TNotifyEvent

Event triggered just after the return of a Mapillary request

// fill a TComboBox with all the sequences
procedure TForm.doEndRequest(sender: TObject);
var i,j:integer;



for i := 0 to FMapillaryLayer.Tiles.Count-1 do
for j :=0 to FMapillaryLayer.Tiles[i].Sequences.Count-1 do
// store name and TMapillarySequence




A heat map is used to represent the intensity of data for geographic points.

Fig. 97 Heatmap layer

TECHeatmapLayer is the class that allows to manage this type of layer.

constructor Create(_FMap: TECNativeMap);
// create heatmap layer
HeatmapLayer := TECHeatmapLayer.Create(map);

procedure Clear;

Erase all of the data

procedure Add(const Latitude, Longitude: double; const value: double = 1);

Add a geographical point and assign it a value, you can make several additions to the same point.

procedure Remove(const latitude, longitude: double; const Value: double);

Delete the Value element if it exists at these coordinates
procedure Update;
After adding the points, call Update to refresh the layer

property AutomaticUpdate: boolean
By default false, switch to true if you want the HeatMap to be automatically updated each time you change it.
property Palette: THeatPalette

Palette allows you to manage the colors that are used to create the gradient


// addColor(Red,Green,Blue,Value)
// Red,Green, and Blue byte 0..255
// value double 0..1

// this is the default palette, you can also use Palette.reset for recreate it

HeatmapLayer.Palette.AddColor(0,0,0,0); // black for value=0
HeatmapLayer.Palette.AddColor(0,0,255,0.1);// blue for value=0.1
HeatmapLayer.Palette.AddColor(0,255,255,0.25); // cyan for value=0.25
HeatmapLayer.Palette.AddColor(0,255,0,0.5); // green for value=0.5
HeatmapLayer.Palette.AddColor(255,255,0,0.75); // yellow for value=0.75
HeatmapLayer.Palette.AddColor(255,0,0,1); // red for value=1

property Visible: boolean ;

property PointIsDisc : boolean;

Determines if the point is displayed in the form of a disc, otherwise it's a square

Fig. 98 Style of the point

Property Radius:integer;

The point size

property Opacity : byte;

Change the opacity of the layer (0 to 100)

property MinZoom: byte ;

Minimum zoom layer to display

property MaxZoom: byte ;

Maximum zoom layer to display

property GroupZIndex: integer ;

ZIndex of the group containing all of the heatmaps, is displayed in ascending order of the ZIndex

property ZIndex: integer ;

ZIndex of the layer compared to the other heatmap

property OnUpdate: TNotifyEvent;

Event fired when the heat map was generated

Weather Layer

By using the services of you will be able to see overlay layers owPrecipitation, owSnow, owClouds, owPressure, owTemp, owWind

map.OpenWeatherTilesLayer.Key := 'your api key';
// see pressure
map.OpenWeatherTilesLayer.Add(owPressure) ;
// see precipitation
map.OpenWeatherTilesLayer.Add(owPrecipitation) ;
// remove pressure
map.OpenWeatherTilesLayer.remove(owPressure) ;
go to page
© 2016 ESCOT-SEP Christophe - Made width Help&Web - RSS - Google+