LazMapViewer
Map provider: OpenStreatMap Mapnik, Open Data Commons Open Database License
About
LazMapViewer is a component for embedding maps obtained from the internet, such as Google maps or OpenStreetView, into a Lazarus form.
The initial version of the package was written by Maciej Kaczkowski and later improved by members of the Lazarus forum. The package is currently maintained by Werner Pamler (wp_xxyyzzz-at-gmx-dot-net).
License: Modified LGPL (with linking exception, like Lazarus LCL).
Download and Installation
Development version
Use an SVN client to download the current trunk version from svn://svn.code.sf.net/p/lazarus-ccr/svn/components/lazmapviewer.
Alternatively download the zipped snapshot from https://sourceforge.net/p/lazarus-ccr/svn/HEAD/tree/components/lazmapviewer/ and unzip it to some folder.
Release version
Occasionally, release versions are made available via the Online Package Manager for easy one-click-installation directly from Lazarus.
Installation
In Lazarus, go to "Package" > "Open Package File .lpk". Navigate to the folder with the LazMapViewer sources, and select lazmapviewerpkg.lkp. Click "Compile", then "Use" > "Install". This will rebuild the IDE (it may take some time). When the process is finished the IDE will restart, and you'll find the MapViewer in the palette Misc.
There are optional, supporting packages to extend the basic functionality. They must be installed in the same way, after lazmapviewerpkg.lpk.
Package file name | Contained components | Purpose |
---|---|---|
lazmapviewerpkg.lpk | TMapView | Main component to display maps |
TMvGeoNames | Access to geo coordinates of cities | |
TMvDEFpc | Default download engine using the internal fpc routines | |
TMvDECache | Offline-only access to map tiles | |
TMvPluginManager | Extends the functionality of TMapView by plugins | |
lazmapviewer_synapse.lpk | TMvDESynapse | Alternative download engine based on the Synapse library |
lazmapviewer_rgbgraphics.lpk | TMvRGBGraphicsDrawingEngine | Alternative drawing engine based on the RGBGraphics package |
lazmapviewer_bgra.lpk | TMvBGRADrawingEngine | Alternative drawing engine based on the BGRABitmap package |
Requirements
Release v0.2.6 was tested successfully with the following tool combinations:
- Laz/main + FPC/main,
- Laz/main + FPC/fixes,
- Laz 3.0 + FPC 3.2.2,
- Laz 2.0.8 + FPC 3.0.4,
- Laz 1.6.4 + FPC 3.0.2 on Windows,
- Laz 3.0 + FPC 3.2.2 on Linux Mint gtk2/gtk3/qt5,
- Laz/main + FPC 3.2.2 on macOS Cocoa.
The Main Map Viewer: TMapView
Getting Started
Here is a short tutorial to create your first map.
Preparation
- Drop a TMapView component on the form and size it as needed. The component is on the Misc tab of the Lazarus component palette.
- With the MapView selected in the object inspector, pick one of the items from the MapProvider combobox, and setActive property to true - after a short time of loading you'll see your first map!
- In the background the MapView component has established an internet connection to the default map provider and downloaded the default map. The maps are delivered as a series of tiled images at given size, usually 256x256 pixels in png or jpeg format.
- When property CacheOnDisk is set to true the tile images are stored in a cache directory and used primarily to reduce internet traffic; only when an image is not found another download from the internet is triggered again. Depending on the CacheLocation property the cache can be set up to be in the user profile (clProfile), the temp (clTemp) or a custom directory (clCustom, path CachePath).
- It is recommended to turn off the Active property during normal work because the time to load the form with the MapView sometimes can be annoyingly long. In order to see the map at run time, you should add the following handler for the form's OnShow or OnActivate event and set the Active there:
procedure TForm1.FormActivate(Sender: TObject);
begin
MapView1.Active := true;
end;
- Another important optimization is to make sure that the UseThreads property is true. This delegates downloading and drawing to several tasks and considerably speeds up viewing of the maps.
- That's all. Compile, run and see your fist map! Use the left mouse button to drag the map to another location, and rotate the mouse wheel to change the magnfication.
Magnification
- The map images are provided in different magnifications depending on the Zoom level of the MapView. Each step in Zoom level corresponds to doubling the magnification. The lowest zoom level (0) displays the entire world in a single tile, level 1 holds a quarter in each tile, etc. The maximum Zoom level for most map providers is 18 -- this means that the most detailed maps cover a fraction of 1/2^18 of the earth circumference, about 150 m (at the equator).
- So, if you want to see more details you must increase the Zoom value of the MapView. You can enter the requested value in the Object Inspector at designtime, or add a TScrollbar or TTrackbar to the form to change the magnfication interactively by using the following simple OnChange event handler (set the Max of the bar to, say, 18:
procedure TForm1.TrackBar1Change(Sender: TObject);
begin
MapView1.Zoom := TrackBar1.Position;
end;
- Alternatively, zooming can also be achieved by rotating the mouse wheel provided that mvoMouseZooming is set in the Options. When ZoomToCursor is true the zoom operation is relative to the mouse cursor, otherwise to the map center.
Location
- By default the center of the map is the intersection of the 0-degree meridian with the equator - which is a bit off of the coast of western Africa. In order to focus onto a different location you must specify its longitude and latitude in the Center property of the MapView. This is a TRealPoint record having the longitude and latitude in the Lon and Lat record elements.
- Suppose we want to zoom into Manhattan. Use your favorite search engine to determine the geo coordinates of Manhattan: Longitude = -73.985130°, Latitude = 40.758896°. Alternatively you can also use the TGeoNames component which comes with the LazMapViewer package and provides access to a database of the geo coordinates of a huge number of locations via internet.
procedure TForm1.FormActivate(Sender: TObject);
var
P: TRealPoint;
begin
MapView1.Active := true;
P.Lon := -73.985130;
P.Lat := 40.758896;
MapView1.Center := P;
end;
- In addition to specifying the location numerically you can also change the location by dragging the map with the mouse (with mvoMouseDragging set in the Options): Just press the left mouse button and slowly move the mouse in the direction where you want to see more - the map will follow. But you should be aware that usually neighboring maps will have to be loaded from the internet, and this may make the entire action a bit sluggish.
- At designtime, you can enter the coordinates in the MapCenter property of the object inspector, and when Active is true you'll get immediate feedback in the map.
Basic Documentation
Properties
These are the main properties of the TMapView component:
- Active (boolean): must be set to true before the MapView component can display maps.
- CacheLocation (enumeration clProfile, clTemp, clCustom): determines whether the directory for the tile image cache is the user profile, the temp directory or a custom directory specified by CachePath
- CacheMaxAge: is the maximum number of days files are kept in the cache. Default: quasi-infinite.
- CacheOnDisk (boolean): when set to true (default) dowloaded map tiles are stored in a directory to reduce internet traffic and for faster access. It is not recommended to turn this property off. Files are deleted from the cache after a storage period of CacheMaxAge days, by default: never.
- CachePath (string): name of the directory in which downloaded map images are buffered when CacheLocation is set to clCustom.
- Cyclic (boolean): When switched to true the map is repeated at the date boundary to completely fill the width of the mapviewer.
- DownloadEngine (TMvCustomDownloadEngine): Determines the control which is responsible for the download of the tile images from the map servers. in Windows, this is by default the TMvDEWin engine based on the WinInet library. Linux and macOS, by default, use the TMvDEFPC engine which takes advantage of the TFPHttpClient class coming with FPC. If a different download engine is required the corresponding component can be hooked to this property. The alternative TMvDESynapse engine based on the Synapse library can be installed as a separate component. Note that there is also a "download" engine TMvDECache which only loads images from the cache but does not actually access the internet - this is interesting when you already have pre-loaded tile images and your application covers only a small region.
- DrawingEngine (TMvCustomDrawingEngine): Painting of the tiled images is a speed-critical task. By default, this is done using routines from the IntfGraphics unit; the corresponding drawing engine is in the TMvInfGraphicsDrawingEngine class. However, it is possible to provide a different drawing engine here. The standard installation of LazMapViewer comes with the alternative TMvRGBGraphicsDrawingEngine which is based on the RGBGraphics package, and with the TMvBGRADrawingEngine utilizing the BGRABitmap package. Since these engines require external libraries they must be installed from separate packages.
- DrawPreviewTiles (boolean): When a tile currently is not available an existing tile is stretched to fit into the tile's place in the map. Otherwise, when DrawPreviewTiles = false, the missing tile's area is filled by the uniform background color of the MapView.
- Layers: a collection of TMapLayer instances which can be drawn independently over the base map - see below.
- MapCenter: a helper class needed for entering the geo coordinates of the map's center point (MapCenter.Latitude and MapCenter.Longitude in the object inspector. When the option mvoLatOnInDMS is set the values can be entered in the degree-minute-second format, otherwise as standard fractional floating point numbers.
- MapProvider (string): This string identifies the provider from which the maps are downloaded. Built-in providers allow to choose between OpenStreetMaps, GoogleMaps, Virtual Earth, Yandex etc. Details are given in section Map Providers.
- Options: a set with the following elements
- mvoMouseDragging: Allows to enable/disable dragging of the map by the mouse. Default: on
- mvoMouseZooming: Allows to enable/disable zooming by rotating the mouse wheel. Default: on
- mvoEditorEnabled: Allows to edit the position of points and tracks with the mouse. Default: off (Note that this feature is not yet fully developed)
- mvoLatLonInDMS: When set, geo coordinates can be displayed/entered in the degree-minute-seconds format, otherwise as standard float number of the degrees. Default: off
- UseThreads (boolean): When set to true downloading and drawing of maps is delegated to multiple threads in order to achieve a smoother response. It is not recommended to turn this property off.
- Zoom (integer): Magnfication of the map: 0 = coarsest magnification, earth view, 17 or 18 = highest magnification. Each zoom step results in doubling of the magnfication factor.
- ZoomMax (integer) and ZoomMin: Specify the range in which the zoom can be varied.
- ZoomToCursor (boolean): When this is true zooming occurs relative to the mouse position, otherwise to the center of the map. This feature is interesting for zooming with the mouse wheel.
Here are properties related to overlayed GPS objects (points of interest, tracks):
- GPSItems: TGPSObjectList: list of GPS objects assigned to the MapView See below for details.
- DefaultTrackColor: TColor, DefaultTrackWidth: Integer: the default color and line width, respectively, of overlayed tracks. See below for details.
- POIImage: TBitmap: bitmap which will be overlayed to identify a "point of interest" (POI).
- POImages: TCustomImageList: image list from which images can be selected as markers for points-of-interest. Note that the LazMapViewer installation comes with a large selection of POI icons in the folder marker-images.
- POITextBgColor: TColor: background color of the overlayed text describing a point of interest. Turn off the text background by selecting clNone.
Events
- OnAfterDrawObjects, OnBeforeDrawObjects: events which are executed after/before overlay objects of the map (points of interest, tracks) are drawn.
- OnCenterMove: fires whenever the Center of the MapView have been changed.
- OnChange: fires whenever the Center, size or Zoom factor of the MapView change.
- OnDrawGpsPoint: see below
- OnZoomChange: fires whenever the Zoom factor of the MapView changes.
- OnMouseDown, OnMouseEnter, OnMouseLeave, OnMouseMove, OnMouseUp: standard mouse events.
Main methods
- procedure GetMapProviders(lstProviders: TStrings): Returns in lstProviders the names of all registered map providers. When one of these strings is assigned to the MapProvider property of the MapVieew the servers of the corresponding provider will become the source of the displayed maps.
- function GetVisibleArea: TRealArea: returns the top/left and bottom/right corner points of the displayed rectangle in geo coordinates. TRealArea is a record consisting of the TRealPoint records TopLeft and BottomRight.
- function LatLonToScreen(aPt: TRealPoint): TPoint: maps a geo point (latitude, longititude) to screen pixels (relative to the TMapView instance).
- function ScreenToLatLon(aPt: TPoint): TRealPoint: maps a the coordinates of a screen pixel (relative to the MapView instance) to geo coordinates (latitude, longiitude). Here is an example how the geo coordinates of the mouse cursor can be displayed in two labels by means of the OnMouseMove event of the MapView:
uses
mvTypes, // for TRealPoint
mvEngine; // for LonToStr() and LatToStr() funtions
procedure TForm1.MapView1MouseMove(Sender: TObject; Shift: TShiftState;
X, Y: Integer);
var
P: TRealPoint;
begin
P := MapView1.ScreenToLatLon(Point(X, Y));
Label1.Caption := 'Longitude: ' + LonToStr(P.Lon, true);
Label2.Caption := 'Latitude: ' + LatToStr(P.Lat, true);
end;
- procedure SaveToFile(AClass: TRasterImageClass; const AFileName: String): Save the currently displayed map as bitmap of the given class to a file. Example for saving to a jpg image:
procedure TForm1.Button1Click(Sender: TObject);
begin
MapView1.SaveToFile(TJpegImage, 'mapview.jpg');
end;
- procedure SaveToStream(AClass: TRasterImageClass; AStream: TStream): Similar to SaveToFile, but output is not in a file, but in the stream provided.
- function SaveToImage(AClass: TRasterImageClass): TRasterImage: creates an instance of the given image class and writes the currently visible view port image to it.
- procedure CenterOnObj(obj: TGPSObj): Centers the map on the provided GPS object. More on GPS objects below.
- function CyclicPointsOf(APoint: TPoint): TPointArray: Calculates the positions of specified point which appear on the reapeted maps in "cyclic" mode.
- procedure ZoomOnArea(const aArea: TRealArea): Adjusts the Zoom level such that the given area is completely shown and fills the component bounds as much as possible.
- procedure ZoomOnObj(obj: TGPSObj): Adjusts the Zoom level such that the given GPS object is completely seen at highest magnification. More about GPS objects below.
Map Providers
The MapView component can display maps from various providers. To activate a given provider its name must be specified in the MapProvider property of the component.
Here is a list of the names of the built-in map providers:
- OpenStreetMap Mapnik (Open Data Commons Open Database License, copyright https://www.openstreetmap.org/copyright/en; the introductory screenshot uses a map of this provider)
- OpenStreetMap Wikipedia
- OpenStreetMap Sputnik
- OpenStreetMap.fr Hot
- OpenStreetMap.fr Cycle Map
- Open Topo Map
- Google Maps (Terms and conditions: https://www.google.com/intl/en_en/help/terms_maps/)
- Google Satellite
- Yandex.Maps (Terms and conditions: https://yandex.ru/legal/maps_termsofuse/index.html)
- Yandex.Maps Satellite
- Virtual Earth Bing
- Virtual Earth Aerial
- Virtual Earth Hybrid
The following providers require an API key to access their service. The API key can be obtained by registering at the provider sites. Usually the API keys are free, however, a charge may be required for commercial usage - please see the details on the provider sites. The strings received as API keys must be stored in global variables of your application.
- Registration at https://www.thunderforest.com/docs/apikeys/, store API key in the variable ThunderForest_ApiKey (Terms and conditions: https://www.thunderforest.com/terms/)
- Open Cycle Map
- OpenStreetMap Transport
- Registration at https://developer.here.com/?create=Freemium-Basic&keepState=true&step=account, store the received APP ID and APP CODE in the variables HERE_AppID and HERE_AppCode, respectively. Terms and conditions: https://legal.here.com/us-en/terms/here-end-user-terms
- Here WeGo Map
- Here WeGo Grey Map
- Here WeGo Reduced Map
- Here WeGo Transit Map
- Here WeGo POI Map
- Here WeGo Pedestrian Map
- Here WeGo DreamWorks Map
- Registration at https://home.openweathermap.org/users/sign_up, store the API Key in variable OpenWeatherMap_ApiKey:
- OpenWeatherMap Clouds
- OpenWeatherMap Precipitation
- OpenWeatherMap Pressure
- OpenWeatherMap Temperature
- OpenWeatherMap Wind
Please respect the license requirements of the map providers.
The Drawing Engine
For flexible output, all the drawing operations are handled by a dedicated class, the "drawing engine". The basic ancestor is TMvCustomDrawingEngine which provides methods for the elemental drawing operations, such as drawing (scaled) bitmaps, writing text, drawing graphics primitives (line, polygon, rectangle, ellipse). This is similar to what TCanvas does in the LCL, but it is more general because basically any graphics library can be interfaced to the mapviewer.
type
TMvCustomDrawingEngine = class(TComponent)
public
function GetCacheItemClass: TPictureCacheItemClass; virtual; abstract;
procedure CreateBuffer(AWidth, AHeight: Integer); virtual; abstract;
procedure DrawBitmap(X, Y: Integer; ABitmap: TCustomBitmap; UseAlphaChannel: Boolean); virtual; abstract;
procedure DrawCacheItem(X, Y: Integer; AImg: TPictureCacheItem; ADrawMode: TItemDrawMode = idmDraw; AOpacity: Single = 1.0); virtual; abstract;
procedure DrawScaledCacheItem(DestRect, SrcRect: TRect; AImg: TPictureCacheItem); virtual; abstract;
procedure Ellipse(X1, Y1, X2, Y2: Integer); virtual; abstract;
procedure FillPixels(X1, Y1, X2, Y2: Integer; AColor: TColor); virtual; abstract;
procedure FillRect(X1, Y1, X2, Y2: Integer); virtual; abstract;
function GetFont: TMvFont;
function GetPen: TMvPen;
procedure Line(X1, Y1, X2, Y2: Integer); virtual; abstract;
procedure Polyline(const Points: array of TPoint); virtual; abstract;
procedure Polygon(const Points: array of TPoint); virtual; abstract;
procedure PolyBezier(const Points: array of TPoint; Filled: Boolean = False; Continuous: Boolean = True); virtual; abstract;
procedure PaintToCanvas(ACanvas: TCanvas); overload;
procedure PaintToCanvas(ACanvas: TCanvas; Origin: TPoint); overload; virtual; abstract;
procedure Rectangle(X1, Y1, X2, Y2: Integer); virtual; abstract;
function SaveToImage(AClass: TRasterImageClass): TRasterImage; virtual; abstract;
procedure SetFont(AFont: TMvFont);
procedure SetFont(AFontName: String; AFontSize: Integer; AFontStyle: TFontStyles; AFontColor: TColor);
procedure SetPen(APen: TMvPen);
procedure SetPen(APenStyle: TPenStyle; APenWidth: Integer; APenColor: TColor);
function TextExtent(const AText: String): TSize; virtual; abstract;
function TextHeight(const AText: String): Integer;
procedure TextOut(X, Y: Integer; const AText: String); virtual; abstract;
function TextWidth(const AText: String): Integer;
property BrushColor: TColor read GetBrushColor write SetBrushColor;
property BrushStyle: TBrushStyle read GetBrushStyle write SetBrushStyle;
property FontColor: TColor read GetFontColor write SetFontColor;
property FontName: String read GetFontName write SetFontName;
property FontSize: Integer read GetFontSize write SetFontSize;
property FontStyle: TFontStyles read GetFontStyle write SetFontStyle;
property PenColor: TColor read GetPenColor write SetPenColor;
property PenStyle: TPenStyle read GetPenStyle write SetPenStyle;
property PenWidth: Integer read GetPenWidth write SetPenWidth;
property Opacity: Single read GetOpacity write SetOpacity;
end;
Since an arbitrary graphics engine does not store images in the LCL classes (TBitmap etc) a more general TPictureCacheItem class was introduced which must be implemented by each drawing engine class.
The default drawing engine is TMvIntfImageDrawingEngine which is based on the routines of FCL-Image and TLazIntfImage. It does not require installation of any third-party packages because everything comes with Lazarus and FPC. However, drawing may be a bit sluggish from time to time. Better performance can be achieved with the TMvRGBGraphicsDrawingEngine or with the TMvBGRADrawingEngine; they require installation of the RGBGraphics or BGRABitmap packages, respectively. They are external, but can be installed from the Online-Package-Manager.
Map Overlays
TMapView is able to overlay various objects on the basic map discussed so far: Points of interest, tracks, areas, even other tile maps. This can be achieved at two levels of access: - a RAD-like approach at designtime in the Object Inspector (TMapXXX classes) - a more direct approach requiring some code (TGPSXXX classes)
Basic overlay architecture
Classes
The public TMapView property GPSItems collects data on points of interest and tracks to be overlayed on the map. The common ancestor type of these overlay items is TGPSObj:
type
TGPSObj = class
...
public
destructor Destroy; override;
property Name: String read FName write FName;
property ExtraData: TObject read FExtraData write SetExtraData;
property IdOwner: Integer read FIdOwner;
property BoundingBox: TRealArea read GetBoundingBox write SetBoundingBox;
property Visible: Boolean read FVisible write FVisible;
property ZOrder: Integer read FZOrder;
end;
- The Name can be displayed near the item in the map.
- ExtraData can be used freely, for example to define the color of a track. An example how this could be done is found in unit mvExtraData, classes TDrawingExtraData and TTrackExtraData.
- IdOwner is the ID of the owner TGPSObj instance to which the current item belongs.
- BoundingBox gives the geo coordinates of the top/left and bottom/right corner points of the rectangle containing all points of the item.
- Visible allows to switch a TGPSObj instance on and off.
- ZOrder determines the order in which various TGPSObj instances are drawn.
A specialized TGPSObj class is a TGPSPoint, for example a scenic "Point-of-Interest" (POI), a way point along a hiking path etc.
TGPSPoint = class(TGPSObj)
public
constructor Create(ALon,ALat: double; AEle: double=NO_ELE; ADateTime: TDateTime=NO_DATE);
class function CreateFrom(aPt: TRealPoint): TGPSPoint;
function HasEle: boolean;
function HasDateTime: Boolean;
function DistanceInKmFrom(OtherPt: TGPSPoint; UseEle: boolean=true): double;
property Lon: Double read GetLon;
property Lat: Double read GetLat;
property Ele: double read FEle;
property DateTime: TDateTime read FDateTime;
property RealPoint: TRealPoint read FRealPt;
end;
In its constructor, the geo coordinates longitude and latitude must be specified. They are also available as read-only properties Lon and Lat, as well as a record RealPoint. Optional parameters are the elevation of the point (property Ele as well as some date/time information (property DateTime), for example the date and time when this point was visited. A special function, DistanceInKmFrom, is available to calculate the on-earth distance from another GPS point.
TGPSPointOfInterest is a further specialization of TGPSPoint. It provides an ImageIndex property to select a marker image from the TMapView's POIImages:
type
TGPSPointOfInterest = class(TGPSPoint)
public
property ImageIndex: Integer read FImageIndex write FImageIndex default -1;
end;
A sequence of TGPSPoint instances sets up a TGPSPolyLine, like a polygon. The individual vertices are collected in the list Points. The TGPSPolyLine provides the method GetArea to determine the enclosing rectangle Area (TRealArea is a record with the top/left and bottom/right corner points in geo coordinates).
type
TGPSPolyLine = class(TGPSObj)
private
FPoints: TGPSPointList;
function GetAllObjs: TGPSObjEnumerator; override;
public
constructor Create;
destructor Destroy; override;
procedure GetArea(out Area: TRealArea); override;
property Points: TGPSPointList read FPoints;
end;
The descendant class TGPSTrack inherits the Points property which is to be understood as the points along a path, for example points of a hike. There is also a DateTime property usable for example to hold the date of the hike; by default it is taken from the DateTime value of the first track point. The function TrackLengthInKm calculates the length of the entire path with the points being connected by straight line segments. There are also properties to determine the order the track is painted on the map:
type
TGPSTrack = class(TGPSPolyLine)
public
function TrackLengthInKm(UseEle: Boolean=true): double;
property DateTime: TDateTime read GetDateTime write FDateTime;
property LineColor: TColor read FLineColor write FLineColor;
property LineWidth: Double read FLineWidth write FLineWidth;
property ConnectColor: TColor read FConnectColor write FConnectColor;
property ConnectWidth: Double read FConnectWidth write FConnectWidth;
property Opacity: Single read FOpacity write FOpacity;
end;
There is also a TGPSArea which is a closed polygon (TGPSLine) and can be filled by a specified color. Note the parameter Opacity for semitransparent rendering so that the underlying map shines through. The user can take advantage of this feature to highlight some areas in the original map.
type
TGPSArea = class(TGPSPolyLine)
public
property FillColor: TColor read FFillColor write FFillColor;
property LineColor: TColor read FLineColor write FLineColor;
property LineWidth: Double read FLineWidth write FLineWidth;
property Opacity: Single read FOpacity write FOpacity;
end;
The TGPSTileLayer, finally, which also descends from TGPSObj, is able to display an entire map of tiles, just like the TMapView. It can have its own setting for the MapProvider and the UseThreads property. The enumerated property DrawMode determines the way how the layer is drawn over the background:
- idmDraw - fully overwrites the background
- idmUseOpacity - uses the Opacity value (a single between 0=transparent) and 1=opaque) for semi-transparent rendering
- idmUseSourceAlpha - some map providers deliver tile images with alpha channel, e.g. OpenRailwayMap which only contains the tracks of railway lines on a transparent background. In this setting the overlay map is perfectly mixed with the background map.
type
TGPSTileLayer = class(TGPSTileLayerBase) // TGPSTileLayerBase inherits from TGPSObj
public
// all the following properties are inherited from the ancestor, TGPSTileLayerBase
property MapProvider; // String
property DrawMode; // TItemDrawMode = (idmDraw, idmUseOpacity, idmUseSourceAlpha)
property Opacity; // Single
property UseThreads; // boolean
All these GPS objects are be stored in a TGPSObjectList:
type
TGPSObjectList = class(TGPSObj)
...
constructor Create;
destructor Destroy; override;
Procedure Clear(OwnedBy: integer);
procedure ClearExcept(OwnedBy: integer; const ExceptLst: TIdArray; out Notfound: TIdArray);
procedure GetArea(out Area: TRealArea); override;
function GetObjectsInArea(const Area: TRealArea): TGPSObjList;
function GetIdsArea(const Ids: TIdArray; AIdOwner: integer): TRealArea;
function Add(aItem: TGpsObj; AIdOwner: integer; AZOrder: integer = 0): integer;
function ChangeZOrder(AItem: TGPSObj; ANewZOrder: Integer): Integer;
procedure Delete(AObj: TGPSObj);
procedure DeleteById(const Ids: Array of integer);
function FindTrackById(const ID: Integer): TGPSTrack;
procedure BeginUpdate;
procedure EndUpdate;
property Count: integer read GetCount;
property Items[AIndex: Integer]: TGpsObj read GetItem; default;
property OnModified: TModifiedEvent read FOnModified write FOnModified;
end;
Objects can be added to the list by calling the Add method. Since each addition is accompanied by a repaint of the MapView you should call BeginUpdate before and EndUpdate after adding the objects. Every added object must be accompanied by an arbitrary numeric ID which is used to group similar objects.
Method DeleteByID deletes all objects sharing the same IDE. Clear, on the other hand, clears all the objects which are owned by object having the specified ID. An extension is ClearExcept which does the same but skips IDs listed in the ExceptLst array.
GetObjectsInArea creates a TFPObjectlist (of type TGpsObjList here) with all the objects enclosed by the given rectangle (Area). GetIDsArea, conversely, returns the rectangle enclosing all the objects with the IDs listed in parameter IDs (which is a simple array here).
The TMapView component provides 10 pre-allocated TGPSObjectList instances which are accessible via the array property GPSLayer[L: Integer]. They serve like a stack of layers drawn the in order of their Z value (see TGPSObj). For historic reasons, the center layer GPSLayer[5] is also made available as property GPSItems.
Examples
Adding a point-of-interest
Suppose you want to mark a special point in the map by a mouse click and identify it with some descriptive text. Write a handler for the OnMouseUp event in which you query the description and then take the pixel coordinates of the clicked point, convert it to geo coordinates and create a TGpsPoint from it which you add to the GpsItems of the MapView:
uses
mvTypes, mvGPSObj;
const
_POI_ = 10;
procedure TForm1.MapView1MouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
var
pt: TRealPoint;
gpsPt: TGpsPoint;
ptName: String;
begin
ptName := '';
if InputQuery('Point of Interest', 'Enter name:', ptName) then
begin
pt := MapView1.ScreenToLonLat(Point(X, Y));
gpsPt := TGpsPoint.CreateFrom(pt, NO_ELE, Now() );
gpsPt.Name := ptName;
Mapview1.GPSItems.Add(gpsPt, _POI_);
end;
end;
In this example the constant _POI_ is used as ID of the added point. By default, the point is drawn as a red cross, the name is drawn underneath it. You can highlight the text by changing its background color in property POITextBgColor of the MapView. You can also replace the default red cross by an arbitrary bitmap which must be loaded into the POIImage property (the folder example of the LazMapViewer installation contains a ready-to-use pin-cushion icon). And when the point is created as an instance of TGPSPointOfInterest rather than TGPSPoint you can select for it an image from the MapView.POIImages image list.
There is also an event, OnDrawGpsPoint, which can be used to replace the entire drawing process by custom routines. The event has the GpsPoint as parameter. The other event parameter is the DrawingEngine which provides methods for drawing; it has been explained above. Here is an example in which at first the screen coordinates of the point are calculated; and then a blue circle is drawn at this position as a marker and labeled by a large bold font:
procedure TForm1.MapView1DrawGpsPoint(Sender: TObject;
ADrawer: TMvCustomDrawingEngine; APoint: TGpsPoint);
const
R = 8; // Circle radius, in pixels
var
p: TPoint;
begin
// Screen coordinates of the GPS point
p := MapView1.LonLatToScreen(APoint.RealPoint);
// Draw a blue circle
ADrawer.PenColor := clBlack;
ADrawer.BrushColor := clBlue;
ADrawer.BrushStyle := bsSolid;
ADrawer.Ellipse(p.X-R, p.Y-R, P.X+R, p.Y+R);
// Draw the point label
ADrawer.BrushStyle := bsClear;
ADrawer.FontName := 'SegoeUI';
ADrawer.FontSize := 16;
ADrawer.FontStyle := [fsBold];
ADrawer.TextOut(
p.X - ADrawer.TextWidth(APoint.Name) div 2,
p.Y + R + 4,
APoint.Name
);
end;
Overlaying another tile layer
The following code displays central Europe and overlays the railway lines from the OpenRailwayMap Standard provider. The usual steps are:
- Create a instance of TGPSTileLayer.
- Set its MapProvider and DrawMode as needed.
- Add it to one of the MapView's GPSLayers. Provide an appropriate ID to identify the layer.
uses
mvGPSObj, mvDrawingEngine;
const
_TILELAYERS_ID_ = 42;
procedure TForm1.FormCreate(Sender: TObject);
var
layer: TGPSTileLayer;
begin
MapView1.MapProvider := 'OpenStreetMap Mapnik';
MapView1.Zoom := 5;
MapView1.MapCenter.Longitude := 10;
MapView1.MapCenter.Latitude := 49;
layer := TGPSTilelayer.Create;
layer.MapProvider := 'OpenRailwayMap Standard';
layer.DrawMode := idmUseSourceAlpha;
MapView1.GPSLayer[0].Add(layer, _TILELAYERS_ID_);
MapView1.Active := true;
end;
Creating overlays at designtime
Classes
In order to facilitate using overlay objects several collections were added to the TMapView so that objects can be added, edited and deleted at designtime. For this reason, the basic list elements descend from TCollection and the elements themselves descend from TCollectionItem. The most fundamental collection here is TMapCollectionBase and its descendant TMapCollection. The most fundamental collection item classes are TMapItem and the derived TMapPoint (corresponding to TGPSPoint) with these most important properties:
type
TMapItem = class(TCollectionItem)
...
public
property View: TMapView read GetView;
property Layer: TMapLayer read GetLayer;
property GPSObj: TGPSObj read GetGPSObj;
published
property Caption: TCaption read FCaption write SetCaption;
property Visible: Boolean read FVisible write SetVisible default True;
end;
TMapPoint = class(TMapItem)
...
public
property LatLonInDMS: Boolean read GetLatLonInDMS;
property RealPoint: TRealPoint read GetRealPoint write SetRealPoint;
property ToScreen: TPoint read GetToScreen;
published
property Longitude: Double read FLongitude write SetLongitude;
property Latitude: Double read FLatitude write SetLatitude;
property Elevation: Double read FElevation write SetElevation stored IsElevationStored;
property DateTime: TDateTime read FDateTime write SetDateTime stored IsDateTimeStored;
end;
More specialized classes are TMapPointOfInterest, TMapTrackPoint and TMapAreaPoint
- TMapPointOfInterest is equivalent to TGPSPointOfInterest and introduces the ImageIndex property.
- TMapTrackPoint corresponds to TGPSTrackpoint. It publishes a new property Mark which determines whether this point is at the begin, end, somewhere in the middle, or between track segments (smStart, smEnd, smMid, smNone, respectively).
- TMapAreaPoint has no analogon in a TGPSXXXX class.
Another imporant descendant of TMapItem is TMapLayer. Similarly to TGPSTileLayer it refers to each one of the layer stacks in the map. Like TGPSTileLayer it can contain complete tile map (based on its MapProvider setting). But it can also consist of points-of-interest, tracks or areas (or a combination of them). The points-of-interest are administrated by the PointsOfInterest collection (type TMapPointsOfInterest), the tracks in the Tracks collection (type TMapTracks), and the areas in the Areas collection (type TMapAreas). The link between the TMapLayer and the GPS-type of objects is established by the class TGPSComboLayer which consists of a TGPSTileLayer for its background and a collection of other objects to draw on top. Actually, the Draw method is overridden to draw first the background and then - the rest of the objects contained into.
The layers themselves are collected in a TMapLayers collection which descends from TMapCollection. The instance Layers is a direct member of TMapView.
type
TMapLayer = class(TMapItem)
...
public
function HitTest(constref Area: TRealArea): TMapObjectList; override;
function AddPointOfInterest(APoint: TRealPoint; ACaption: String = ''): TMapPointOfInterest;
procedure AssignFromGPSList(AList: TGPSObjectList);
property ComboLayer: TGPSComboLayer read FComboLayer;
published
property MapProvider: String read GetMapProvider write SetMapProvider;
property UseThreads: Boolean read GetUseThreads write SetUseThreads default True;
property DrawMode: TItemDrawMode read FDrawMode write SetDrawMode default idmUseOpacity;
property Opacity: Single read FOpacity write SetOpacity default 0.25;
property PointsOfInterest: TMapPointsOfInterest read GetPointsOfInterest write SetPointsOfInterest;
property Areas: TMapAreas read GetAreas write SetAreas;
property Tracks: TMapTracks read GetTracks write SetTracks;
end;
Examples
Overlaying at designtime a second map as a layer
- Click on the '...' button next to the Layers property to open the layer editor.
- Click on '+ Add' to create a new layer and to add it.
- Specify the map provider in the MapProvider property.
- Moreover, select the DrawMode: If the images received from the map server have their own transparency in an alpha channel the option idmUseSourceAlpha is the correct one. Otherwise, maybe you want the original map base layer to shine through the overlay layer - select idmUseOpacity and adjust the Opacity property as needed.
Overlaying at designtime a layer with points-of-interest
- Open the layer editor and add a new layer Or, if you want to add the points to an existing layer, select this layer in the layer editor.
- Click on the '...' button next to the PointsOfInterest item among the layer's properties in the object inspector.
- This opens the points editor where you can enter the point properties, in particular the Latitude and Longitude. Caption specifies the text to be displayed below the point symbol. If an imagelist is attached to the MapView's POIImages property you can also pick one of these icons by means of the ItemIndex property.
- Repeat with all other points that you want to add.
In an analogous way you can also define the points of a track or of an area.
Plugins
(to be written>
Finding Geo Locations: TMvGeoNames
TMvGeoNames is a component which searches the geo coordinates (longitude, latitude) of many cities or other locations in the world. Using the download engine of the MapView component it sends a query to the site http://geonames.org/search.html and analyzes the returned html string.
Getting Started
- Assuming that you already have set up an application for the TMapView component as described above, you simply drop a TMvGeoNames component on the form; it is located next to the TMapView icon in the Misc component palette.
- Add a button which is supposed to trigger the search.
- Write an OnClick handler for the button which calls the TMvGeoNames method Search. Enter the search destination as first parameter, and the download engine to be used as second parameter. The function returns the found longitude and latitude as a TRealPoint record which can be passed immediately to the Center property of the MapView component. This way, the viewer can immediately jump to the found location:
procedure TForm1.Button1Click(Sender: TObject);
begin
MapView1.Center := MvGeoNames1.Search('Grand Canyon National Park', MapView1.DownloadEngine);
end;
Documentation
Methods
- function Search(ALocationName: String; ADownloadEngine: TMvCustomDownloadEngine): TRealPoint: searches the requested location using the specified download engine. Return value are the geo coordinates of the location given as a TRealPoint. The search returns also locations with similar names, however, they must be extracted by handling the OnNameFound event.
Properties
- LocationName: string: Is the name of the (first) found location as listed by the geonames site.
Events
- OnNameFound: This event is triggered while the received response of the geonames server is being analyzed. The html string usually contains a variety of locations. Whenver a location has been extracted from the received string the event is fired and tells the name, a description, and the longitude/latitude TRealPoint record for this location. Handling this event makes it possible to list all the found locations for example in a combobox or listbox to give the user the opportunity to select any one of them:
type
TGeoLocation = class
Name: String;
Descr: String;
Coords: TRealPoint;
end;
procedure TForm1.MVGeoNames1NameFound(const AName: string;
const ADescr: String; const ALoc: TRealPoint);
var
loc: TGeoLocation;
begin
loc := TGeoLocation.Create;
loc.Name := AName;
loc.Descr := ADescr;
loc.Coords := ALoc;
ComboBox1.Items.AddObject(AName, loc);
end;