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
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
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.
2To 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) ;