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

// Delphi map component ECMap

var ecNativeLineToRoute : TecNativeLineToRoute;
ecNativeLineToRoute := TecNativeLineToRoute.create;

// edit route
ecNativeLineToRoute.Line := map.shapes.Lines[0];

// end edit route
ecNativeLineToRoute.Line := nil;

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. 1 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. 2 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;


Instead, use the OverPassApi layer


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';

If you do not specify node in your query the areas will also be displayed.


To hide them define the style

map.Styles.addRule('#XAPI.polygone {visible:false}');

A point is added to the center of the surfaces, to not display it, add the style

map.Styles.addRule('#XAPI.marker {if:polyid>-1;visible:false}');

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. 3 'restaurant|bar|cafe'

Fig. 4 '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. 5 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. 6 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.

FMapillaryLayer := TECMapillaryLayer.Create(map);

FMapillaryLayer.OnClick := doOnMapillaryLayerClick;
FMapillaryLayer.OnTrafficSignClick := doOnMapillaryLayerTrafficSignClick;

FMapillaryLayer.OnBeginRequest := doBeginRequest;
FMapillaryLayer.OnendRequest := doEndRequest;

FMapillaryLayer.LocalCache := ExtractfilePath(ParamStr(0)) + 'cache';

FMapillaryLayer.Visible := true;

Fig. 7 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. 8 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.

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

FMappilaryLayer.Visible := true;

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. 9 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. 10 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) ;

TomTom Traffic Incidents

Unlike tomtom's incident tiles, this layer allows you to get information about incidents by hovering the mouse or by pressing your finger.

Fig. 11 TomTom Traffic Incidents

// get your key at
map.TomTom.Key := 'your api key';
map.TomTom.Incident.Layer := true;


property CategoryLabel : TStringList

This list contains the type of incident, the title is included in the information heading.
You can use it to make a translation but make sure to respect the order and the number of elements

property CacheTime : int64

Time in milliseconds during which the information is kept in memory, by default 180000 (3mn)

property Layer : boolean

Activate / Deactivate the layer

property InfoStyle : TInfoWindowStyle

Style of infoWindow, default iwsRectangle

property FontColor : TColor

Text color, default black

property BorderColor : TColor

Border color, default to a light gray

property Break : integer

Spacing in pixels between paragraphs, default 7

property Tab : integer

Width in pixels of the first column, default 40

property TextDelay : string

Label 1st paragraph, default "delay" (use TextXXX fields for your translation)

property TextLength : string

Label 2nd paragraph, default "Length

property TextFrom : string

Label 3rd paragraph, default "From

property TextTo : string

Label 4th paragraph, default "To

property Width : integer

Width infoWindow, default 240

property YAnchor : integer

Vertical offset of the infoWindow, default -25

property OnClick : TOnShapeMouseEvent

Event triggered by a click on an incident.
map.TomTom.Incident.OnClick := doIncidentClick;
// show all properties
procedure TForm1.doIncidentClick(sender: TObject; const item: TECShape);
var Key, Value, content: string;
win: TECShapeInfoWindow;
content := '';
if item.PropertiesFindFirst(Key, Value) then
// if necessary line break
if content<>'' then content := content+'<br>';
// align the values to 110 pixels
Key := Key + '<tab=110>';
// Bold the keys
content := content + '<b>' + Key + '</b>: ' + Value ;
// continue as long as there are properties
until item.PropertiesFindNext(Key, Value);

// create window if not exists
if map['info'].InfoWindows.count = 0 then
win := map['info'].AddInfoWindow;
win.Width := 350;
win := map['info'].InfoWindows[0];

win.content := content;
win.SetPosition(map.MouseLatLng.Lat, map.MouseLatLng.lng);
win.Visible := true;

Fig. 12 Incident OnClick

Styling the information

property Style : string

Contains the styles assigned to the lines.

By default as for tomtom tiles the colors are functions of the magnitude of delay

'#TOMTOM-INCIDENT.line.magnitudeOfDelay:0 {color:#BFBFBF}';
'#TOMTOM-INCIDENT.line.magnitudeOfDelay:1 {color:#F58240}';
'#TOMTOM-INCIDENT.line.magnitudeOfDelay:2 {color:#EB4C13}';
'#TOMTOM-INCIDENT.line.magnitudeOfDelay:3 {color:#AB0000}';
'#TOMTOM-INCIDENT.line.magnitudeOfDelay:4 {color:#8B837D}';

The infoWindow has properties that will allow you to set a style in response

  • iconCategory : 0-14 index on the CategoryLabel list
  • delay : number of seconds
  • length : number of meters
// default style
// style if the wait is more than 5mn (delay>300)

OverPassApi layer

This layer has the same utility as the XAPI layer but it uses OverPassApi which is a newer technology and should therefore be seen as the replacement for the XAPI layer.

The purpose is to extract specific data from OpenStreetMap located in the displayed area of your map, an automatic refresh takes place after each move.

If you define a local cache the searches will be saved there and will be available in offline mode.


Fig. 13 Demo OverPassLayer

procedure Amenity(value: string;const Data:TSetOSMData=[odNode,odWay]);

Display amenities equal to Value.
Data indicates if we are looking for the nodes and/or the Ways

procedure Amenity(values: array of string;const Data:TSetOSMData=[odNode,odWay]; const Op: TBinaryFilterOSM = bfOr);

Search for multiple amenities at once

Amenity() procedures are a simplification of Tag() procedures, with a tag_key implicitly equal to "amenity"


procedure Tag(const key, value: string;const Data:TSetOSMData=[odNode,odWay]);

Show Key tag equal to Value.
Data indicates whether to search for Nodes and/or Ways.

procedure Tag(const key:string;const values: array of string;const Data:TSetOSMData=[odNode,odWay]; const Op: TBinaryFilterOSM = bfAnd);

Search for Key tags with multiple Values
op=bfAnd return tags only if they have all values
op=bfOr selects the tag if it has at least one Value

procedure Tag(const tags: array of string;const Data:TSetOSMData=[odNode,odWay]; const Op: TBinaryFilterOSM = bfAnd);

Search for a array of Key=Value

property Query: string

You define your request freely, the bbox and the out will be added automatically
// search Way 'highway'='residential
// search Way 'highway'='residential' OR 'highway'='primary'
//search Way width highway=residential AND name='Park Avenue'
map.OverPassApi.layer.Tag(['highway','residential','name','Park Avenue'],[odWay],bfAnd);
// search Nodes and Way amenity=parking
// search only Nodes amenity=restautant or amenity=parking
// search nodes and ways amenity=parking
map.OverPassApi.layer.Query := 'nw[amenity=parking]';

property Group: TECShapes

Groupe which will be used to display the elements of the OverPassApi layer

property Visible: boolean

Activate/deactivate the layer

property TimeOut: integer

Maximum duration of the request in seconds, by default 60, set to 0 to not indicate a timeout

property OnBeginQuery: TNotifyEvent

Event triggered just before the query is launched

property OnEndQuery: TNotifyEvent

Event triggered when the request is returned

property OnClick: TOnShapeMouseEvent

Event triggered by a click on a layer element

property OnData:TECOnOverPassLayerData

Event triggered when OpenStreetMap XML data is available
map.OverPassApi.Layer.OnBeginQuery := doOnBeginQuery;
map.OverPassApi.Layer.OnEndQuery := doOnEndQuery;
map.OverPassApi.Layer.OnClick := doOnClick;
map.OverPassApi.Layer.OnData := doOnData;
// fired when xml data ready
procedure TForm9.doOnData(const XmlValue:string);
XmlData.Lines.Text := XmlValue;
// fired when click on ovepassapi layer item
procedure TForm9.doOnClick(sender: TObject; const item: TECShape);
var Key, Value, content: string;
win: TECShapeInfoWindow;
content := '';

if item.PropertiesFindFirst(Key, Value) then
// if necessary line break
if content<>'' then content := content+'<br>';
// align the values to 100 pixels
Key := Key + '<tab=100>';
// Bold the keys
content := content + '<b>' + Key + '</b>: ' + Value ;
// continue as long as there are properties
until item.PropertiesFindNext(Key, Value);

// create window if not exists
if map.OverPassApi.Layer.Group.InfoWindows.count = 0 then
win := map.OverPassApi.Layer.Group.AddInfoWindow;
win.Width := 270;
win := map.OverPassApi.Layer.Group.InfoWindows[0];

win.content := content;
win.SetPosition(map.MouseLatLng.Lat, map.MouseLatLng.lng);
win.Visible := true;


// start query
procedure TForm9.doOnBeginQuery(sender : TObject);
QuerySearch.Visible := true;
// end query
procedure TForm9.doOnEndQuery(sender : TObject);
QuerySearch.Visible := false;

Bubble Layer

A bubble layer displays symbols (circles or other shapes) and possibly labels. Size and color can be controlled by measurements, labels by properties. The position can be specified as a location or as latitude and longitude values.

Fig. 14 Demo Bubble Layer


Layer Manager

function Add(const Name: string): TECBubbleLayer;
Add a layer
procedure Delete(const Name: string);
procedure Delete(const index: integer);
Destroy a layer specified by its name or index
procedure Clear;
Delete all layers
function getLayers(const aList: TStrings): integer;
Fills aList with the list of layers and returns their number
property Color: TColor
Default color for elements
property UseColorPalette: boolean
If true layer palette is used, the color of the elements will depend on their size
property Count: integer
number of layers
property MaxItemSize: integer
property MinItemSize: integer
the size of the elements will be scaled between MinItemSize and MaxItemSize pixels
property Layer[const index: string]: TECBubbleLayer
property List[const index: integer]: TECBubbleLayer
Returns a layer according to its name or index
property Opacity: byte
Opacity of the elements
property shape: TPOIShape
shape of element (poiEllipse, poiStar, poiRect, poiTriangle, poiOwnerDraw,
poiHexagon, poiDiamond, poiText, poiArrow, poiArrowHead, poiCross,
poiDiagCross, poiDirectionSign)
property OnChange: TNotifyEvent
Triggered when a layer is added or deleted
property OnEditBubble: TECOnEditBubble
Triggered after the creation of an element, it allows to modify its properties (shape, color,...)
property OnClick: TOnShapeMouseEvent
Click on an element
property OnRightClick: TOnShapeMouseEvent
Right click on an element

You can find most of its properties at the Layer level, which allows you to centralize them.

Map.BubbleLayers.Color := clGreen;
Map.BubbleLayers.OnEditBubble := doOnEditBubble;
Map.BubbleLayers.OnClick := doOnClick;
Map.BubbleLayers.OnRightClick := doOnRightClick;
Map.BubbleLayers.OnChange := doOnChangeLayers;

// click on bubble
procedure TForm.doOnClick(sender: TObject; const item: TECShape);
caption := TECBubbleLayer(sender).Name + ' : ' + item.PropertyFormat(item.Hint);

// right click on bubble
procedure TForm.doOnRightClick(sender: TObject; const item: TECShape);
caption := TECBubbleLayer(sender).Name + ' : ' + item.PropertyFormat(item.Hint)+' (Right)';

// event triggered after the automatic creation
// you can modify your element as you wish, to change its shape and color for example
procedure TForm.doOnEditBubble(const BubbleShape:TECShapePoi);

if BubbleShape['shape']= '1' then
BubbleShape.POIShape := poiDiamond;
BubbleShape.Color := getInvertColor(BubbleShape.color);


// event triggered after add or delete TECBubbleLayer
procedure TForm.doOnChangeLayers(sender : TObject);
Map.BubbleLayers.getLayers(Layers.Items) ;
delete.Enabled := layers.ItemIndex>-1;


procedure Clear;
Deletes all the elements of the layer
function Add(const Lat, Lng, Size: double;const properties:string=''): TECBubbleItem;
function Add(const location: string; Size: double;const properties:string=''): TECBubbleItem;
Add an item by its GPS position or by its address

procedure Delete(const index: integer);

procedure fitBounds;

Adjusts the zoom to show all the elements
procedure Update;
Updates the display of the layer, as long as you do not call Upate the changes are not reflected on the map.

property Color: TColor

property UseColorPalette: boolean

property ColorPalette: THeatPalette

Palette that calculates a color based on the scale of the element

Fig. 15 Default color palette

// addColor(Red,Green,Blue,Value)
// Red,Green, and Blue byte 0..255
// value double 0..1,
// value corresponds to a percentage
// 0.5 is equivalent to an element that is half the size of the largest

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

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

property Count: integer ;

property HintProperty: string
Property that will serve as a Hint for the element.
property Hint : string
Default Hint for elements

property MaxItemSize: integer

property MinItemSize: integer

property Labels: TLabelShape

property Opacity: byte

property shape: TPOIShape

property MaxZoom: byte
property MinZoom: byte
Layer display zoom range

property Name: string

property List[const index: integer]: TECBubbleItem

List of element
property ZIndex: integer

// Attention each request of localization makes an Internet request,
// in the cumulated it takes time
// the best is to use directly the GPS coordinates
// here it is used for documentation purposes
procedure TForm10.Add_5_Most_Populated_FR_cities;
var FBubbleLayer : TECBubbleLayer;

FBubbleLayer := Map.BubbleLayers.Add('5 Most Populated FR cities');
// Global Hint where the properties of each element will be injected
FBubbleLayer.Hint := '[size] inhabitants in [location] in 2020';
// the color will be determined according to the size using the layer's color palette
FBubbleLayer.UseColorPalette := true;
// the size of the elements will be scaled between 60 and 100 pixels depending on size
FBubbleLayer.MinItemSize := 60;
FBubbleLayer.MaxItemSize := 100;
// labels will be displayed for zoom 5 and more
FBubbleLayer.Labels.MinZoom := 5;
// the label will be composed of the properties 'location' and 'size'
FBubbleLayer.Labels.LabelMask := '[location]'+#13#10+'[size]';
FBubbleLayer.Labels.LabelType := ltMask;

// the position is determined by their location
// its size by the number of inhabitants


// Add('location',size) automatically sets the 'location' and 'size' properties
// you can add properties with these two syntaxes
// var bubble:TECBubbleItem;
// bubble := FBubbleLayer.Add('Paris,FR',2145906,'prop1=data1,prop2=data2');
// bubble['propx'] := 'datax';

// update of the layer display
// zoom in to show all elements


Chart Layer

This layer allows you to display pie charts or stacked bars.

Fig. 16 Vertical stacked bars

Fig. 17 Pie charts

Fig. 18 "Donut" type pie charts


TECChartLayer Manager

function Add(const Name: string): TECChartLayer;
Add a layer
procedure Delete(const Name: string);
procedure Delete(const index: integer);
Destroy a layer specified by its name or index
procedure Clear;
Destroys all layers
function getLayers(const aList: TStrings): integer;
Fills aList with the list of layers and returns their number
property Count: integer
Number of layers
property MaxItemSize: integer
property MinItemSize: integer
For circular diagrams this indicates the maximum and minimum radius, the length for bars
the size of the elements will be scaled between MinItemSize and MaxItemSize pixels depending on the total value of the fields
property BarThickness : integer
Bar thickness, default 16 pixels
property Layer[const index: string]: TECChartLayer
property List[const index: integer]: TECChartLayer
Returns a layer according to its name or index
property Opacity: byte
Opacity of the elements
property ChartType: TECChartType
Shape of diagrams (ctPie, ctDonut, ctFillDonut,ctVerticalStackedBar,ctHorizontalStackedBar)
property OnChange: TNotifyEvent
Triggered when a layer is added or deleted
property OnClick: TOnShapeMouseEvent
Click on a diagram
property OnRightClick: TOnShapeMouseEvent
Right click on a diagram

You can find most of its properties in the layer, which allows you to centralize them.

// click on chart
procedure TFormChartLayer.doOnClick(Sender: TObject; const item: TECShape);
caption := 'Click Left - '+TECchartLayer(Sender).Name +
' : Chart n°'+inttostr(item.IndexOf)+' Total =' + item['total'] ;
// if needed you can access TECChartItem with TECChartItem(item.item)
// right click on chart
procedure TFormChartLayer.doOnRightClick(Sender: TObject; const item: TECShape);
caption := 'Click Right - '+TECchartLayer(Sender).Name +
' : Chart n°'+inttostr(item.IndexOf)+' Total =' + item['total'] ;

procedure TFormChartLayer.FormCreate(Sender: TObject);
map.ChartLayers.OnChange := doOnChangeLayers;
map.ChartLayers.OnClick := doOnClick;
map.ChartLayers.OnRightClick := doOnRightClick;

// The size of the diagrams will also be adapted according to the zoom
map.ScaleMarkerToZoom := true;

tag := 0;
TotalLayer := 0;


procedure Clear;
Clears all diagrams in the layer
function AddField(const Legend: string; Color: TColor): integer;
Adds a field to the diagrams
function Add(const Lat, Lng: double; const Properties: string = '') : TECChartItem;
Adds a diagram to the geographical position Lat,Lng
procedure Delete(const index: integer);
Delete a diagram
procedure fitBounds;
Zoom in on the layer diagrams
procedure Update;
Creation of all diagrams, as long as Update is not called the changes are not reflected.
property Caption: string
Title of your layer, you can use it to caption

property BarThickness : integer

property Color: TColor

property BorderColor: TColor

property Count: integer
Number of chart
property Fields: TECAChartField
All the fields in your diagrams

property HintColor: TColor

property Labels: TLabelShape
management of labels

property MaxChartSize: integer

property MinChartSize: integer ;

property Opacity: byte

property ChartType: TECChartType

property MaxZoom: byte

property MinZoom: byte

property Visible : boolean

property Name: string

property List[const index: integer]: TECChartItem ;

List of diagrams, access it to fill in the field values
property Sort: TECChartDataSort
By default the field values are output in descending order, you can choose between cdsNone, cdsAscending et cdsDescending

property ZIndex: integer

property OnClick: TOnShapeMouseEvent

property OnRightClick: TOnShapeMouseEvent

property OnValidHint : TOnValidHint

// palettes from
palettes : array [0..2]of array [0..7] of string = (


// change default hint legend
procedure doOnValideHint(const Sender: TECChartItem; const index: integer;
const percent, value: double; var hint: string);

// a positive index indicates a data line
if index >-1 then

hint := Sender.Layer.Fields[index].Legend + ' : ' + doubletostrdigit(percent,
1) + ' % (' + doubletostr(value) + ')'

else // -1 indicates that the legend is complete, you can enrich it

Hint := '<h3><center>'+sender.Layer.Caption+ '</center></h3>'+
'<h4><center>Chart n°'+inttostr(sender.Shape.IndexOf)+'</center></h4>';


// create random layer
procedure TFormChartLayer.addLayerClick(Sender: TObject);
Lat, Lng: double;
x, y, i,id_pal, delta_lat, delta_lng: integer;
s: string;
FChartLayer: TECChartLayer;
FChartType: TECChartType;
chart: TECChartItem;

// cycle chart type
case tag of
FChartType := ctPie;
s := 'Pie';
FChartType := ctDonut;
s := 'Donut';
FChartType := ctFillDonut;
s := 'FillDonut';
FChartType := ctVerticalStackedBar;
s := 'VerticalStackedBar';
FChartType := ctHorizontalStackedBar;
s := 'HorizontalStackedBar';


tag := tag + 1;
if tag>4 then tag := 0;

// create layer
FChartLayer := map.ChartLayers.Add(s + ' ' + timeTosTr(time));

FChartLayer.Caption := 'Title Chart Layer ' + inttostr(TotalLayer);

// adapt the graph legend
FChartLayer.OnValidHint := doOnValideHint;

// create random datas

// select palette
id_pal := random(3);

// between 4 and 8 lines of data
for i := 0 to 3 + random(5) do
FChartLayer.AddField('Data ' + chr(i + ord('A')), StrToColor(palettes[id_pal][i]));

FChartLayer.ChartType := FChartType;

// labels will be displayed for zoom 3 and more
FChartLayer.Labels.MinZoom := 3;

if FChartLayer.ChartType < ctVerticalStackedBar then
// max radius 50
FChartLayer.MaxChartSize := 50;
FChartLayer.Labels.Align := laCenter;
else // stacked bar
// max bar size 100
FChartLayer.MaxChartSize := 100;
FChartLayer.Labels.Align := laBottom;

FChartLayer.MinChartSize := 16;

// distribute 4 * 4 graphs on the visible surface of the map
delta_lat := round(((map.NorthEastLatitude - map.SouthWestLatitude) * 1000));
delta_lng := round(((map.NorthEastLongitude - map.SouthWestlongitude) * 1000));

for y := 0 to 3 do
for x := 0 to 3 do

Lat := map.SouthWestLatitude + (random(delta_lat) / 1000);
Lng := map.SouthWestlongitude + (random(delta_lng) / 1000);

// create chart
chart := FChartLayer.Add(Lat, Lng);

// add ramdom value
// automatically sets the 'total' propertie
for i := low(FChartLayer.Fields) to High(FChartLayer.Fields) do[i] := random(100) + random(99);


// generate the elements on the map

// zoom it

layers.ItemIndex := layers.Items.Count - 1;
Delete.Enabled := true;



This layer displays the rate of air pollution in the world, it uses data from the projet World Air Quality Index

Fig. 19 Demo AirQuality

procedure getJSON(const Lat, Lng: double)

Request data from the closest station to the GPS point

procedure getJSON(const city: string)

Requests the data of a station whose name is indicated

procedure getJSON(const Lat, Lng, lat2, lng2: double)

Request the stations that are in the indicated area

The data is returned in JSON format, the OnJson event is triggered as soon as it is available.
The OnRequest event is triggered when the request is launched.

procedure TFormAirQuality.FormCreate(Sender: TObject);
// get your free key from
map.AirQuality.key := '';
map.AirQuality.OnJson := doOnJson;
map.AirQuality.OnClick := doOnclick;
map.AirQuality.OnRequest := doOnRequest;
map.AirQuality.visible := true;

/// Request the list of stations located in the visible area of the map.
/// The list is returned in json format, the OnJson event is triggered as soon as it is available
procedure TFormAirQuality.getallstationsClick(Sender: TObject);

/// Request data from the station closest to the center of the map
/// Data is returned in json format, the OnJson event is triggered as soon as it is available
procedure TFormAirQuality.GetStationClick(Sender: TObject);

/// Triggered when the response of a call to getJSON is available
/// The response is contained in Json, Query contains the parameters of the request
procedure TFormAirQuality.doOnJson(sender: TObject; const Query, JSon: string) ;
MJson.Lines.Text := Json;

/// Triggered just before call getJSON or click on Station
procedure TFormAirQuality.doOnRequest(sender : TObject);
MJson.Lines.Text := 'result pending...';

function JsonToAirQualityCity(const json:string):TAirQualityCity

Processes the json data obtained by GetJSon to fill a record TAirQualityCity
TAirQualityCity = record
Name: string;
latitude: double;
longitude: double;
DominantPollutant: TAirQualityItem;
Pollutants: TAirQualityPollutants;
Weather: TAirQualityWeather;
IsoTime: string;
Time: TDateTime;
level: TAirQualityLevel;
LevelColor: TColor;

property AirQualityIndex: TAirQualityIndex

Choice of the main index displayed on the tiles

By default usepa_aqi which indicates the main pollutant

TAirQualityIndex = (usepa_aqi, usepa_pm25, usepa_10, usepa_o3, usepa_no2,usepa_so2, usepa_co, asean_pm10);

property Key: string

Fill in this property with the free api key that you get at this address

property Visible: boolean

Manage the layer display.

property City: TAirQualityCity

Data of the last station flown or clicked.

For the request to be triggered, OnClick or OnHover must be set.


property OnJson: TAirQualityOnJson

Event triggered when the data requested by GetJSON is available

property OnClick : TAirQualityEvent

Event triggered when clicked station data is available

property OnHover : TAirQualityEvent

Event triggered when data from the overflown station is available

property OnRequest : TNotifyEvent

Event triggered when a query is run and before the result is available
// Triggered after a click on a station when data is available
procedure TFormAirQuality.doOnclick(const sender : TAirQualityCity);
var i:integer;
// all data in json format are available in sender.Json

case sender.level of
aqlGood: level := 'Good';
aqlModerate: level := 'Moderate';
aqlUnhealthySensitive: level := 'Unhealthy for Sensitive Groups';
aqlUnhealthy: level := 'Unhealthy';
aqlVeryUnhealthy: level := 'Very unhealthy';
aqlHazardous: level := 'Hazardous';

MJSon.text := #13#10+Level+#13#10#13#10;


MJson.Lines.Add('IQ '+DoubleToStr(Sender.DominantPollutant.Value));

for i := 0 to High(sender.Pollutants) do
MJson.Lines.Add(Sender.Pollutants[i].Name+' = '+DoubleToStr(sender.Pollutants[i].value));

for i := 0 to High( do
MJson.Lines.Add([i].Name+' = '+DoubleToStr([i].value));


MJSon.Color := sender.LevelColor;
MJSon.Font.Color := GetContrastingColor(sender.LevelColor);
go to page
Réalisé avec la version gratuite pour les particuliers d'Help&Web