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;
You can either plug into OnMapHiResChange or overload the doOnMapHiResChange(sender: TObject) procedure so that your layer adapts to change resolution
1Panoramio
Google closed Panoramio November 4, 2016, the service is no longer functional
1A 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;
...
ecNativeLineToRoute.free;
The event OnPanoramioClick(sender: TObject; const Item: TECShape; const ownerId, ownerName, PhotoId, PhotoDate, PhotoTitle, PhotoUrl, copyright: string) is raised when clicking a Panoramio item.
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
1Example: 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;
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
2XapiLayer 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
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]'
map.XapiLayer.Search := 'node[highway=bus_stop]';
For nodes you can use simplified syntax.
map.XapiLayer.Search := 'highway=bus_stop';
For the key amenity you can simplify even more.
map.XapiLayer.Search := 'restaurant|bar|cafe';
If you do not specify node in your query the areas will also be displayed.
3To hide them define the style
A point is added to the center of the surfaces, to not display it, add the style
property Visible : boolean
property MaxItem : integer
property OnClick : TOnShape
property OnChange : TNotifyEvent
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
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;}');
Way
With way[key = value] you can retrieve paths.
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;}');
You can also find the junctions between routes.
map.XapiLayer.Search := 'way[highway=unclassified|road|motorway|trunk|primary|secondary|tertiary|residential]';
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.
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!
1Mapillary 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
2Use TECMapillaryLayer to display a Mapillary layer.
FMapillaryLayer.OnClick := doOnMapillaryLayerClick;
FMapillaryLayer.OnTrafficSignClick := doOnMapillaryLayerTrafficSignClick;
FMapillaryLayer.OnBeginRequest := doBeginRequest;
FMapillaryLayer.OnendRequest := doEndRequest;
FMapillaryLayer.LocalCache := ExtractfilePath(ParamStr(0)) + 'cache';
FMapillaryLayer.Visible := true;
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
3procedure SearchBounds(NorthEastLatitude, NorthEastLongitude, SouthWestLatitude, SouthWestLongitude: double);
function DetectionsImage(const image_id:int64;const List:TListDataDetections):integer;
id,
value,
create_at,
geometry : string;
end;
Fill the list with all the items detected in the image, returns the number of items
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 ;
property Tiles: TMapillaryTiles
a TMapillaryTile contains a property Sequences : TMapillarySequences which is a list of TMapillarySequence which itself is a list of TMapillaryImage
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
id : int64;
First_seen_at,
last_seen_at : TDateTime;
lat,lng : double;
value: string;
end;
procedure Clear;
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
4procedure ClearAll;
property Visible : boolean
property TrafficSignVisible : boolean
Displays or hides signage
Triggered when clicking on a mapillary image
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;
Triggered when clicking on a traffic mapillary element
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;
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.
6FMappilaryLayer.AccessToken := 'HERE-YOUR-TOKEN';
FMappilaryLayer.Visible := true;
property OnBeginRequest: TNotifyEvent
property OnEndRequest: TNotifyEvent
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.
TECHeatmapLayer is the class that allows to manage this type of layer.
constructor Create(_FMap: TECNativeMap);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);
After adding the points, call Update to refresh the layer
property AutomaticUpdate: boolean
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
Property Radius:integer;
The point size
Change the opacity of the layer (0 to 100)
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// 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.
map.TomTom.Key := 'your api key';
map.TomTom.Incident.Layer := true;
TECTomTomIncident
property CategoryLabel : TStringList
You can use it to make a translation but make sure to respect the order and the number of elements
property CacheTime : int64
property Layer : boolean
property InfoStyle : TInfoWindowStyle
property FontColor : TColor
property BorderColor : TColor
property Break : integer
property Tab : integer
property TextDelay : string
property TextLength : string
property TextFrom : string
property TextTo : string
property Width : integer
property YAnchor : integer
property OnClick : TOnShapeMouseEvent
// 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;
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
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.
7procedure Amenity(value: string;const Data:TSetOSMData=[odNode,odWay]);
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);
Amenity() procedures are a simplification of Tag() procedures, with a tag_key implicitly equal to "amenity"
8procedure Tag(const key, value: string;const Data:TSetOSMData=[odNode,odWay]);
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);
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);
property Query: string
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
property Visible: boolean
property TimeOut: integer
property OnBeginQuery: TNotifyEvent
property OnEndQuery: TNotifyEvent
property OnClick: TOnShapeMouseEvent
property OnData:TECOnOverPassLayerData
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;