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

Layers

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
private
FObserver : TNativeMapObserver;
FShapes : TECShapes;
FMap : TNativeMapControl;

FOnShapeRightClick,
FOnShapeClick : TOnShapeMouseEvent;

FOnMouseClick,
FOnMouseMove : TNotifyEvent;

function getMinZoom : byte;
function getMaxZoom : byte;

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

function getVisible : boolean;

protected

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;


public


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;

end;



TECNativePlaceLayer

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

1

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 := 'http://www.helpandweb.com/cake_32.png';
// lat,lng on center of image
FPlacesLayer.XAnchor := 16;
FPlacesLayer.YAnchor := 16;
// hi-res image 64*64
FPlacesLayer.MarkerHiResFilename := 'http://www.helpandweb.com/cake_64.png';
// lat,lng on center of image
FPlacesLayer.HiResXAnchor := 32;
FPlacesLayer.HiResYAnchor := 32;
...

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

// 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
begin

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

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

end;


if FPlacesLayer.Shapes.InfoWindows.Count=0 then
begin
FPlacesLayer.Shapes.InfoWindows.add(0,0,'');
FPlacesLayer.Shapes.InfoWindows[0].zindex := 100;
end;

FPlacesLayer.Shapes.InfoWindows[0].content := Content;
FPlacesLayer.Shapes.InfoWindows[0].SetPosition(Shape.Latitude,shape.Longitude);
FPlacesLayer.Shapes.InfoWindows[0].Visible := true;


end;


end;

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

Instead, use the OverPassApi layer

1

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.

2

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);
begin
//
end;

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

Use the styles to decorate your items

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

map.styles.addRule(Selector+':restaurant' +
'{graphic:base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAAaBAMAAABI' +
'sxEJAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAABJQTFRFAAAA////AAAAAAA'
+ 'AAAAAAAAA/h6U3wAAAAV0Uk5TAAAQgL++EJOXAAAAOElEQVQY02MIFQ0MFWRUCXVigLBcQ0OgrNDQUH'
+ 'JYQIDOMg0NDYawBIFC2FgCSCxBerFMg0Es02AAP34wMx8/aIAAAAAASUVORK5CYII=;visible:true;');

map.styles.addRule(Selector+':bar' +
'{graphic:base64,iVBORw0KGgoAAAANSUhEUgAAABoAAAAaCAYAAACpSkzOAAAAzklEQVRIx+2WUQ3'
+ 'FIAxFK2ES8HANVMIkIGUOkICESUDCJCABCdtPSQgJC2UjeXlZk3402e1ZoRSIGgZgA3AqfSOtAYgDoK'
+ 'iFmCxWaDLMaEAsoqDQBNGsGlDeHyexnbJPAHwpKuIe9zSwDFzFPR6egM4P9HMgX3YQgDSr67gQrsq51'
+ 'z8ZqqqSwHrOkhsZqguAo0iyS5weL1kD5qZUcjPNfXV1HPIThmaY9vr4QH8KknbPXbfMgtiqvSMA+1Zy'
+ 'lrNy9/SK8g0PVamc2NlTK98F1MyKB+QkmGEAAAAASUVORK5CYII=;visible:true;}');

map.styles.addRule(Selector+':cafe' +
'{graphic:base64,iVBORw0KGgoAAAANSUhEUgAAABoAAAAaCAYAAACpSkzOA'
+ 'AAAuUlEQVRIx2NgIACMjY0F0PgN+OTJBkCD9iMbCsT/cclTYkkCzGCoJfuB+D02eUosKQAZAjIM5nKQ'
+ 'JUBsgE2eEotQDIHyHXDJU2JRPyn8wQmgcfCfTPye1HghGw8/i4D4PBrbgVYWYcMGtLAoAakUmQ8VW0/'
+ 'TOALyFXDGHZUtEqCXRbCy8DzNLAKlNqTMn0ATi0AVI5LYflol7/1I7Pk4a18q5Zv3BKsPKpUMCvQogh'
+ '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. 2 'restaurant|bar|cafe'

Fig. 3 'highway=bus_stop'

Way

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. 4 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. 5 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;
map.XapiLayer.search := 'highway=bus_stop';
// ! call bound after set search !
map.XapiLayer.bound(43,0.7,44,0.8);
...
TForm.XapiChange(sender : TObject);
begin
// here xapilayer contain openstreetmap data
// copie in another group
map.group[copy_xapi'].ToTxt := map.XapiLayer.shapes.ToTxt;
end;

Mapillary

Only available for Delphi 10.x and higher!

1

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 www.mapillary.com, 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

2

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. 6 Demo Mapillary

TECMapillaryLayer

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

3

Fig. 7 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
id,
value,
create_at,
geometry : string;
end;

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;
try
n := FMapillaryLayer.DetectionsImage(SelectedSequence[SelectedImageIndex].Id,L);
Detections.Lines.BeginUpdate;

for i := 0 to n-1 do
Detections.Lines.Add(L[i].value);

Detections.Lines.EndUpdate;
finally
L.Free;
end;

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

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

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

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

4

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

5

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;
begin
// show photo in a TImage
bmp := TBitmap.Create;
try
// load image 256*256
// also url1024 and url2048

if FMapillaryLayer.LoadMapillaryBitmap(MapillarySequence[ImageIndex].Url256,bmp) then
Image.Picture.Assign(bmp);

finally
bmp.Free;
end;

end;
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) ;
begin
FMapillaryLayer.OpenWindow(item.Latitude,item.Longitude,
'<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
);
end;
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.

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

begin

ComboSequences.Items.BeginUpdate;
ComboSequences.items.clear;

for i := 0 to FMapillaryLayer.Tiles.Count-1 do
for j :=0 to FMapillaryLayer.Tiles[i].Sequences.Count-1 do
// store name and TMapillarySequence
ComboSequences.items.addObject(FMapillaryLayer.Tiles[i].Sequences[j].Sequence_id,FMapillaryLayer.Tiles[i].Sequences[j]);

ComboSequences.Items.EndUpdate;

end;

Heatmap

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

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


HeatmapLayer.Palette.Clear;

// 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. 9 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 OpenWeathermap.org 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. 10 TomTom Traffic Incidents

// get your key at https://developer.tomtom.com/user/register
map.TomTom.Key := 'your api key';
map.TomTom.Incident.Layer := true;

TECTomTomIncident

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;
begin
content := '';
if item.PropertiesFindFirst(Key, Value) then
begin
repeat
// 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);
end;

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

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

Fig. 11 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{weight:8;bsize:2;hbcolor:light(black,128);bcolor:light(black,48);penStyle:dot;}';
'#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
map.Styles.addRule('#TOMTOM-INCIDENT.infowindow{bcolor:light(gray,128);color:white;fontcolor:black}');
// style if the wait is more than 5mn (delay>300)
map.Styles.addRule('#TOMTOM-INCIDENT.infowindow{if:delay>300;bcolor:dark(red,128);color:light(red,96);fontcolor:white}');

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.

7

Fig. 12 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"

8

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
map.OverPassApi.layer.Tag('highway','residential',[odWay]);
// search Way 'highway'='residential' OR 'highway'='primary'
map.OverPassApi.layer.Tag('highway',['residential','primary'],[odWay],bfOr);
//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
map.OverPassApi.layer.Amenity('parking']);
// search only Nodes amenity=restautant or amenity=parking
map.OverPassApi.layer.Amenity(['restaurant','parking'],[odNode]);
// 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);
begin
XmlData.Lines.Text := XmlValue;
end;
// fired when click on ovepassapi layer item
procedure TForm9.doOnClick(sender: TObject; const item: TECShape);
var Key, Value, content: string;
win: TECShapeInfoWindow;
begin
content := '';

if item.PropertiesFindFirst(Key, Value) then
begin
repeat
// 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);
end;

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

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

end;

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

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. 13 Demo Bubble Layer

TECBubbleLayers

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.

9
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);
begin
caption := TECBubbleLayer(sender).Name + ' : ' + item.PropertyFormat(item.Hint);
end;

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

// 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);
begin

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

end;

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

TECBubbleLayer

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. 14 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;
begin

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

FBubbleLayer.Add('Paris,FR',2145906);
FBubbleLayer.Add('Marseille,FR',870321);
FBubbleLayer.Add('Lyon,FR',522228);
FBubbleLayer.Add('Toulouse,FR',498003);
FBubbleLayer.Add('Nice,FR',343477);

// 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
FBubbleLayer.Update;
// zoom in to show all elements
FBubbleLayer.fitBounds;


end;

Chart Layer

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

Fig. 15 Vertical stacked bars

Fig. 16 Pie charts

Fig. 17 "Donut" type pie charts

TECChartLayers

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.

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

procedure TFormChartLayer.FormCreate(Sender: TObject);
begin
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;
end;

TECCharLayer

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

const
// palettes from https://colorbrewer2.org
palettes : array [0..2]of array [0..7] of string = (
('#7fc97f','#beaed4','#fdc086','#ffff99','#386cb0','#f0027f','#bf5b17','#666666'),
('#d53e4f','#f46d43','#fdae61','#fee08b','#e6f598','#abdda4','#66c2a5','#3288bd'),
('#66c2a5','#fc8d62','#8da0cb','#e78ac3','#a6d854','#ffd92f','#e5c494','#b3b3b3')

);

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

// 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>'+
hint+
'<h4><center>Chart n°'+inttostr(sender.Shape.IndexOf)+'</center></h4>';

end;

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

// cycle chart type
case tag of
0:
begin
FChartType := ctPie;
s := 'Pie';
end;
1:
begin
FChartType := ctDonut;
s := 'Donut';
end;
2:
begin
FChartType := ctFillDonut;
s := 'FillDonut';
end;
3:
begin
FChartType := ctVerticalStackedBar;
s := 'VerticalStackedBar';
end;
else
begin
FChartType := ctHorizontalStackedBar;
s := 'HorizontalStackedBar';
end;

end;

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

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

inc(TotalLayer);
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
begin
// max radius 50
FChartLayer.MaxChartSize := 50;
FChartLayer.Labels.Align := laCenter;
end
else // stacked bar
begin
// max bar size 100
FChartLayer.MaxChartSize := 100;
FChartLayer.Labels.Align := laBottom;
end;

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
begin
for x := 0 to 3 do
begin

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
chart.data[i] := random(100) + random(99);

end;
end;

// generate the elements on the map
FChartLayer.Update;

// zoom it
FChartLayer.fitBounds;

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

end;

AirQuality

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

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

11
procedure TFormAirQuality.FormCreate(Sender: TObject);
begin
// get your free key from https://aqicn.org/data-platform/token/
map.AirQuality.key := '';
map.AirQuality.OnJson := doOnJson;
map.AirQuality.OnClick := doOnclick;
map.AirQuality.OnRequest := doOnRequest;
map.AirQuality.visible := true;
end;

/// 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);
begin
map.AirQuality.getJSON(map.SouthWestLatitude,map.SouthWestLongitude,map.NorthEastLatitude,map.NorthEastLongitude);
end;

/// 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);
begin
map.AirQuality.getJSON(map.Center.Lat,map.Center.Lng);
end;

/// 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) ;
begin
MJson.Lines.Text := Json;
end;

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

function JsonToAirQualityCity(const json:string):TAirQualityCity

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

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 aqicn.org/data-platform/token/

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.

12

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;
level:string;
begin
// all data in json format are available in sender.Json

MJSon.text := #13#10+ map.AirQuality.Legend[sender.Level]+#13#10#13#10;

MJSon.Lines.Add(Sender.Name);

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(sender.weather) do
MJson.Lines.Add(sender.weather[i].Name+' = '+DoubleToStr(sender.weather[i].value));

MJSon.Lines.Add('');
MJSon.Lines.Add(sender.IsoTime);
MJSon.Lines.Add(DateTimeToStr(sender.Time));

MJSon.Color := sender.LevelColor;
MJSon.Font.Color := GetContrastingColor(sender.LevelColor);
end;

Layer WMS

The WMSLayers property of the TECNativeMap component allows you to manage a WMS layer list.

varWMS_Cadastre : TECNativeWMS;
...
WMS_Cadastre := map.WMSLayers.Add('https://geobretagne.fr/geoserver/cadastre/wms', // url service
'CP.CadastralParcel', // layer
'CADASTRE' // TECNativeMap group name
);
TECWMSLayers
function Count: integer;
procedure Refresh;
procedure Clear;
function Add(const Url, layers, Name: string): TECNativeWMS;

You can specify several layers, separated by commas.

13
procedure Delete(const Name: string);
procedure Delete(const index: integer);
procedure Delete(const index: TECNativeWMS);
function IndexOf(const Name: string): integer;
property Layer[const index: integer]: TECNativeWMS; default
property Layer[const index: string]: TECNativeWMS; default

property OnCapabilities : TNotifyEvent

Triggered when the service data associated with a layer are available.

property OnFeatureInfo : TNotifyEvent

Triggered when information on a layer element is available

property OnEnabled : TNotifyEvent

Triggered when map view enters or leaves layer coverage area

property OnChangeTimeDimension

Triggered at each period change when the layer's TimeDimension is activated

property OnLoadingTimeDimension

Triggered when layer TimeDimension tiles are loaded, LoadingPercentTimeDimension is 100 if all tiles are available
map.WMSLayers.OnLoadingTimeDimension := doOnLoadingTimeDimension;
...
// event triggered when TimeDimension tiles is loading
procedure TFormWMS_WFS.doOnLoadingTimeDimension(Sender: TObject);
var
WMSLayer: TECNativeWMS;
begin
if Sender is TECNativeWMS then
begin

WMSLayer := Sender as TECNativeWMS;
if assigned(WMSLayer) then
begin
TimeLoading.Position := WMSLayer.LoadingPercentTimeDimension;
TimeLoading.Visible := WMSLayer.LoadingPercentTimeDimension< 100;
end;
end
end;

property OnEnabledTimeDimension

Triggered on activation/deactivation of layer TimeDimension
// connect WMS events
map.WMSLayers.OnCapabilities := doOnCapabilities;
map.WMSLayers.OnFeatureInfo := doOnFeatureInfo;
map.WMSLayers.OnEnabled := doOnEnabled;
map.WMSLayers.OnChangeTimeDimension := doOnChangeTimeDimension;
map.WMSLayers.OnEnabledTimeDimension := doOnEnabledTimeDimension;
...
// OnEnabled is triggered when the visible area of the map moves in or out of the layers.
// When Enabled is false, no more requests are made to the server.
procedure TFormWMS_WFS.doOnEnabled(Sender: TObject);
var
WMSLayer: TECNativeWMS;
begin

if Sender is TECNativeWMS then
begin

WMSLayer := Sender as TECNativeWMS;
if assigned(WMSLayer) then
begin
events.lines.Add(WMSLayer.Name + ' ENABLED : ' +
BoolToStr(WMSLayer.Enabled));
end;
end

end;

// event triggered each time the TimeDimension enabled/disable
procedure TFormWMS_WFS.doOnEnabledTimeDimension(Sender: TObject);
var
WMSLayer: TECNativeWMS;
begin

if Sender is TECNativeWMS then
begin

WMSLayer := Sender as TECNativeWMS;
if assigned(WMSLayer) then
begin
events.lines.Add(WMSLayer.Name + ' TIMEDIMENSION ENABLED : ' +
BoolToStr(WMSLayer.TimeDimension.Enabled));
end;
end

end;


// event triggered each time the TimeDimension period changes
procedure TFormWMS_WFS.doOnChangeTimeDimension(Sender: TObject);
var
WMSLayer: TECNativeWMS;
begin

if Sender is TECNativeWMS then
begin

WMSLayer := Sender as TECNativeWMS;
if assigned(WMSLayer) then
begin
Time.Caption := WMSLayer.Time;
end;
end

end;


// A GetCapabilities request is automatically made to the server,
// when the data is available OnCapabilities is triggered
// and the data is available in the Capabilities string.
// You can restart a request using the GetCapabilities procedure
procedure TFormWMS_WFS.doOnCapabilities(Sender: TObject);
var
WMSLayer: TECNativeWMS;
begin

if Sender is TECNativeWMS then
begin

WMSLayer := Sender as TECNativeWMS;
if assigned(WMSLayer) then
begin

// show Bounding Box
events.lines.Add(WMSLayer.Name + ' BBOX :' +
doubletoStrDigit(WMSLayer.SWLat, 6) + ' ' +
doubletoStrDigit(WMSLayer.SWLng, 6) + ' ' +
doubletoStrDigit(WMSLayer.NELat, 6) + ' ' +
doubletoStrDigit(WMSLayer.NELng, 6));

end;
end

end;

// A GetFeatureInfo request is automatically made to the server,
// when the data is available OnFeatureInfo is triggered
// and the data is available in the FeatureInfo string.
// You can restart a request using the GetFeatureInfo procedure
procedure TFormWMS_WFS.doOnFeatureInfo(Sender: TObject);
var
WMSLayer: TECNativeWMS;
begin

if Sender is TECNativeWMS then
begin

WMSLayer := Sender as TECNativeWMS;
if assigned(WMSLayer) then
begin
events.lines.Add('GET FEATUREINFO : ' + WMSLayer.Name);
end;
end

end;

TECNativeWMS
property Capabilities: string;
Returns data describing service content and accepted parameters.
They are retrieved in a Thread, the event TECWMSLayers.OnCapabilities is activated when they are available.
property FeatureInfo: string;
returns information about an object represented on the map.
They are retrieved in a Thread, the event TECWMSLayers.OnFeatureInfo is activated when they are available.
property layers: string
Requested layer(s)
property Styles: string
list of styles used for layers
property Time: string

Allows you to specify a date in ISO 8601 string format.

Leave blank for latest data

property TimeDimension : TECTimeDimension
Allows you to change the Time property on a time interval
var datetime:TDateTime;
// set Minute and seconde to 0
datetime := SetHourMinuteSeconde(Now, -1, 0, 0);
// show last hour
WMS_Layer_Radar.TimeDimension.StartTime := decHour(datetime, 1);
WMS_Layer_Radar.TimeDimension.EndTime := datetime;
// period 5 minutes
WMS_Layer_Radar.TimeDimension.PeriodMillisecondes := 5 * 60 * 1000;
WMS_Layer_Radar.TimeDimension.TransitionMillisecondes := 500;
// You can also directly pass an array of TDateTime
// WMS_Layer_Radar.TimeDimension.AddTime([datetime1, datetime2,..,datetimex]);
// loop
WMS_Layer_Radar.TimeDimension.Loop := true;
// activate timedimension
WMS_Layer_Radar.EnabledTimeDimension := true;

procedure StartTimeDimension;

Display first date

procedure EndTimeDimension;

Display last date

procedure NextTimeDimension;

Display next date

procedure PrevTimeDimension;

Display previous date

property PauseTimeDimension: boolean

Enable/Disable pause

Fig. 19 Precipitation radar TimeDimension

property Url: string
Server url
property Version: string
String containing the server version in the form '1.0.0', default '1.1.0'.
property Legend: boolean
Displays an image containing the legend of the returned data
property LegendWidth: integer
property LegendHeight: integer
property LegendOpacity: byte
Set legend opacity from 0 to 100
property LegendPosition: TLegendPosition
Choose from : pTopLeft, lpTopRight, lpBottomLeft, lpBottomRight, lpTopCenter, lpBottomCenter, lpLeftCenter, lpRightCenter
property LegendXMargin: integer
Pixel margin for horizontal offset from map edge
property LegendYMargin: integer
Margin for vertical offset from card edge
property LayersList : TAECRecordLayers

TECRecordLayer array containing information on the layers available on the server. This array is filled after the call to getCapabilities, so you can access it as soon as OnCapabilities has been triggered.

getCapabilities is automatically called when the layer is created.

14

TECRecordLayer = record
Name : string;
Title : string;
NELat,
NELng,
SWLat,
SWLng : double;
end;

See WFS-WMS-Capabilities-List for an example of use

Fig. 20 WMS layers list

property Name: string
Name of the group containing the layer data
property Visible : boolean
Layer activation
WMS_Layer_Radar := map.WMSLayers.Add('https://maps.dwd.de/geoserver/ows', // url service
'dwd:Niederschlagsradar', // layer
'RADAR' // TECNativeMap group name
);

WMS_Layer_Radar.Version := '1.3.0';
WMS_Layer_Radar.ZIndex := 20;
WMS_layer_Radar.Legend := true;
// opacity 0..100
WMS_layer_Radar.LegendOpacity := 75;
WMS_layer_Radar.LegendPosition := lpRightCenter;
WMS_Layer_Radar.Visible := true;

Layer WFS

The WFSLayers property of the TECNativeMap component allows you to manage a WFS layer layer.

These layers also have a table giving access to all available features..

property FeatureTypeList : TAECRecordFeatures
TECRecordFeature = record
FType : string;
Name : string;
Title : string;
NELat,
NELng,
SWLat,
SWLng : double;
end;

See WFS-WMS-Capabilities-List for an example of use

Fig. 21 WFS features list

The WFS service must be able to export in JSON format so that TECNativeMap can interpret it!

2
var WFS_Layer_Us : TECNativeWFS;
...
WFS_Layer_Us := map.WFSLayers.Add(
'https://geoserver.geoplatform.gov/geoserver/ngda/ows', // url service
'ngda:473c080c_8686_41d6_b1ee_6945e5c924f3' // layer
,'US-STATICAL-AREA' // TECNativeMap group name
);

// respond to a click on a layer element
WFS_Layer_Us.OnShapeClick := doShapeClick;

// default values for polygones
map.Styles.addRule('#US-STATICAL-AREA.polygone {weight:1;color:black;}');
// when a polygon is hovered over with the mouse, the outline thickness is 3 pixels
// the fill color is red
map.Styles.addRule('#US-STATICAL-AREA.polygone:hover {weight:3;hcolor:red;}');
// polygons with an 'lsad' property value of 'M1' are greyed out
map.Styles.addRule('#US-STATICAL-AREA.polygone.lsad:M1 {fcolor:gray;hbcolor:light(gray)}');
// polygons with an 'lsad' property value of 'M2' are blue
map.Styles.addRule('#US-STATICAL-AREA.polygone.lsad:M2 {fcolor:blue;hbcolor:light(blue)}');

WFS_Layer_Us.MaxFeature := 10000;

// Limit queries to the area bounded by the North-East and South-West corners
// NELat = 50 , NELng = -63
// SWLat = 31 , SWLng = -121
// To accept requests for the whole world (default) : WFS_Layer_Us.BoundingBox;
WFS_Layer_Us.BoundingBox(50,-63,31,-121) ;

// No query if zoom > 10
WFS_Layer_Us.MaxZoom := 10;

// Round off the search area on the corners of the tiles,
// this will allow caching and limit requests to the server.
WFS_Layer_Us.RoundBoxToTiles := true;

// Each time the map is moved, the new zone is queried (taking into account the various limits).
WFS_Layer_Us.AutoRefresh := true;

WFS_Layer_Us.visible := true;

// respond to a click on a layer element
procedure TFormWMS_WFS.doShapeClick(sender: TObject; const item: TECShape);
var Key, Value, content: string;
win: TECShapeInfoWindow;
begin
if not assigned(item) then exit;

content := '';
// extract all properties and their values,
// enriching them for a more readable display
if item.PropertiesFindFirst(Key, Value) then
begin
repeat
// 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);
end;

if content='' then exit;

// retrieve the WFSLayer stored in the Data property of the clicked element's group
if item.Group.Data is TECNativeWFS then
begin
// we use its infoWindow, but we could use any infoWindow,
// in which case we'd have to ensure that its group ZIndex is greater than that of the WFSLayer
win := TECNativeWFS(item.Group.Data).InfoWindow;
win.content := content;
win.SetPosition(map.MouseLatLng.Lat, map.MouseLatLng.lng);
win.Visible := true;
end;
end;

Fig. 22 WFS layer

go to page
Réalisé avec la version gratuite pour les particuliers d'Help&Web