LazMapViewer: what changed
Revision 0 to 1. Removed lines have a minus sign; added lines have a plus sign.
Title: LazMapViewer
| Old | New | Line | |
|---|---|---|---|
| 1 | + | <figure> | |
| 2 | + | <img src="lazmapviewer.png" title="lazmapviewer.png" /> | |
| 3 | + | <figcaption>lazmapviewer.png</figcaption> | |
| 4 | + | </figure> | |
| 5 | + | ||
| 6 | + | ## About | |
| 7 | + | ||
| 8 | + | **LazMapViewer** is a component for embedding maps obtained from the internet, such as Google maps or OpenStreetView, into a Lazarus form. | |
| 9 | + | ||
| 10 | + | 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). | |
| 11 | + | ||
| 12 | + | License: Modified LGPL (with linking exception, like Lazarus LCL). | |
| 13 | + | ||
| 14 | + | ## Download and Installation | |
| 15 | + | ||
| 16 | + | **Development version** | |
| 17 | + | ||
| 18 | + | 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. | |
| 19 | + | ||
| 20 | + | **Release version** | |
| 21 | + | ||
| 22 | + | Occasionally, release versions are made available via the [[Online Package Manager]] for easy one-click-installation directly from Lazarus. | |
| 23 | + | ||
| 24 | + | **Installation** | |
| 25 | + | ||
| 26 | + | 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**. | |
| 27 | + | ||
| 28 | + | There are optional, supporting packages to extend the basic functionality. They must be installed in the same way, after `lazmapviewerpkg.lpk`. | |
| 29 | + | ||
| 30 | + | | Package file name | Contained components | Purpose | | |
| 31 | + | |----|----|----| | |
| 32 | + | | lazmapviewerpkg.lpk | TMapView | Main component to display maps | | |
| 33 | + | | | TMvGeoNames | Access to geo coordinates of cities | | |
| 34 | + | | | TMvDEFpc | Default download engine using the internal fpc routines | | |
| 35 | + | | lazmapviewer_synapse.lpk | TMvDESynapse | Alternative download engine based on the Synapse library | | |
| 36 | + | | lazmapviewer_rgbgraphics.lpk | TMvRGBGraphicsDrawingEngine | Alternative drawing engine based on the RGBGraphics package | | |
| 37 | + | | lazmapviewer_bgra.lpk | TMvBGRADrawingEngine | Alternative drawing engine based on the BGRABitmap package | | |
| 38 | + | ||
| 39 | + | **Requirements** | |
| 40 | + | ||
| 41 | + | 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, with Laz 3.0 + FPC 3.2.2 on Linux Mint gtk2/gtk3/qt5, and with Laz/main + FPC 3.2.2 on MacOS cocoa. | |
| 42 | + | ||
| 43 | + | ## The Main Map Viewer: TMapView | |
| 44 | + | ||
| 45 | + | ### Getting Started | |
| 46 | + | ||
| 47 | + | Here is a short tutorial to create your first map. | |
| 48 | + | ||
| 49 | + | #### Preparation | |
| 50 | + | ||
| 51 | + | - Drop a `TMapView` component on the form and size it as needed. The component is on the *Misc* tab of the Lazarus component palette. | |
| 52 | + | - Add this handler for the form's `OnShow` or `OnActivate` event and set the `Active` property of the `MapView` component to `true`; in principle, this should be possible also in the object inspector, however, does not work at the time of this writing. | |
| 53 | + | ||
| 54 | + | ``` Pascal | |
| 55 | + | procedure TForm1.FormActivate(Sender: TObject); | |
| 56 | + | begin | |
| 57 | + | MapView1.Active := true; | |
| 58 | + | end; | |
| 59 | + | ``` | |
| 60 | + | ||
| 61 | + | - That's all. Compile, run and see your fist map! | |
| 62 | + | - 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, and usually in png format. They are stored in a cache directory (property `CachePath`) and used primarily to reduce internet traffic; only when an image is not found another download from the internet is triggered again. | |
| 63 | + | ||
| 64 | + | #### Magnification | |
| 65 | + | ||
| 66 | + | - 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 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. | |
| 67 | + | - 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, 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: | |
| 68 | + | ||
| 69 | + | ``` Pascal | |
| 70 | + | procedure TForm1.TrackBar1Change(Sender: TObject); | |
| 71 | + | begin | |
| 72 | + | MapView1.Zoom := TrackBar1.Position; | |
| 73 | + | end; | |
| 74 | + | ``` | |
| 75 | + | ||
| 76 | + | - Alternatively, zooming can also be achieved by rotating the **mouse wheel**. | |
| 77 | + | ||
| 78 | + | #### Location | |
| 79 | + | ||
| 80 | + | - 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. | |
| 81 | + | - 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. | |
| 82 | + | ||
| 83 | + | ``` Pascal | |
| 84 | + | procedure TForm1.FormActivate(Sender: TObject); | |
| 85 | + | var | |
| 86 | + | P: TRealPoint; | |
| 87 | + | begin | |
| 88 | + | MapView1.Active := true; | |
| 89 | + | P.Lon := -73.985130; | |
| 90 | + | P.Lat := 40.758896; | |
| 91 | + | MapView1.Center := P; | |
| 92 | + | end; | |
| 93 | + | ``` | |
| 94 | + | ||
| 95 | + | - In addition to specifying the location numerically the you can also change location by **dragging the map with the mouse**. 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 makes the entire action a bit sluggish. | |
| 96 | + | ||
| 97 | + | ### Basic Documentation | |
| 98 | + | ||
| 99 | + | <figure> | |
| 100 | + | <img src="tmapview_200.png" title="tmapview_200.png" /> | |
| 101 | + | <figcaption>tmapview_200.png</figcaption> | |
| 102 | + | </figure> | |
| 103 | + | ||
| 104 | + | #### Properties | |
| 105 | + | ||
| 106 | + | These are the main properties of the `TMapView` component: | |
| 107 | + | ||
| 108 | + | - `Active (boolean)`: must be set to `true` before the `MapView` component can display maps. | |
| 109 | + | - `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. The cache is not deleted automatically. | |
| 110 | + | - `CachePath (string)`: name of the directory in which downloaded map images are buffered. | |
| 111 | + | - `DownloadEngine (TMvCustomDownloadEngine)`: By default, the `TFPHttpClient` is employed to download the images from the map servers. If a different download engine is required the corresponding component can be hooked to this property. The standard installation of the *LazMapViewer* package contains the default `TMvDEFPC` downloader (based on FPC's `TFPHttpClient`) and the `TMvDESynapse` based on the *Synapse* library). | |
| 112 | + | - `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. | |
| 113 | + | - `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. | |
| 114 | + | - `Options = [mvoMouseDragging, mvoMouseZooming]`: Allows to disable dragging of the map with the mouse and zooming with the mouse wheel when the corresponding option is removed from the set (in v0.2.7 and newer). Default: both options active. | |
| 115 | + | - `UseThreads (boolean)`: When set to `true` downloading and drawing of maps is delegated to several threads in order to achieve a smoother response. It is not recommended to turn this property off. | |
| 116 | + | - `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. | |
| 117 | + | - `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. | |
| 118 | + | ||
| 119 | + | Here are properties related to overlayed GPS objects (points of interest, tracks): | |
| 120 | + | ||
| 121 | + | - `GPSItems: TGPSObjectList`: list of GPS objects assigned to the MapView See below for details. | |
| 122 | + | - `DefaultTrackColor: TColor`, `DefaultTrackWidth: Integer`: the default color and line width, respectively, of overlayed tracks. See below for details. | |
| 123 | + | - `POIImage: TBitmap`: bitmap which will be overlayed to identify a "point of interest" (POI). | |
| 124 | + | - `POITextBgColor: TColor`: background color of the overlayed text describing a point of interest. Turn off the text background by selecting `clNone`. | |
| 125 | + | ||
| 126 | + | #### Events | |
| 127 | + | ||
| 128 | + | - `OnCenterMove`: fires whenever the `Center` of the `MapView` have been changed. | |
| 129 | + | - `OnChange`: fires whenever the `Center`, size or `Zoom` factor of the `MapView` change. | |
| 130 | + | - `OnDrawGpsPoint`: see below | |
| 131 | + | - `OnZoomChange`: fires whenever the `Zoom` factor of the `MapView` changes. | |
| 132 | + | - `OnMouseDown, OnMouseEnter, OnMouseLeave, OnMouseMove, OnMouseUp`: standard mouse events. | |
| 133 | + | ||
| 134 | + | #### Main methods | |
| 135 | + | ||
| 136 | + | - `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. | |
| 137 | + | - `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`. | |
| 138 | + | - `function LonLatToScreen(aPt: TRealPoint): TPoint`: maps a geo point (longitude, latitude) to screen pixels (relative to the `TMapView` instance). | |
| 139 | + | - `function ScreenToLonLat(aPt: TPoint): TRealPoint`: maps a the coordinates of a screen pixel (relative to the `MapView` instance) to geo coordinates (longitude, latitude). 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`: | |
| 140 | + | ||
| 141 | + | ``` Pascal | |
| 142 | + | uses | |
| 143 | + | mvTypes, // for TRealPoint | |
| 144 | + | mvEngine; // for LonToStr() and LatToStr() funtions | |
| 145 | + | ||
| 146 | + | procedure TForm1.MapView1MouseMove(Sender: TObject; Shift: TShiftState; | |
| 147 | + | X, Y: Integer); | |
| 148 | + | var | |
| 149 | + | P: TRealPoint; | |
| 150 | + | begin | |
| 151 | + | P := MapView1.ScreenToLonLat(Point(X, Y)); | |
| 152 | + | Label1.Caption := 'Longitude: ' + LonToStr(P.Lon, true); | |
| 153 | + | Label2.Caption := 'Latitude: ' + LatToStr(P.Lat, true); | |
| 154 | + | end; | |
| 155 | + | ``` | |
| 156 | + | ||
| 157 | + | - `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: | |
| 158 | + | ||
| 159 | + | ``` Pascal | |
| 160 | + | procedure TForm1.Button1Click(Sender: TObject); | |
| 161 | + | begin | |
| 162 | + | MapView1.SaveToFile(TJpegImage, 'mapview.jpg'); | |
| 163 | + | end; | |
| 164 | + | ``` | |
| 165 | + | ||
| 166 | + | - `procedure SaveToStream(AClass: TRasterImageClass; AStream: TStream)`: Similar to `SaveToFile`, but output is not in a file, but in the stream provided. | |
| 167 | + | - `function SaveToImage(AClass: TRasterImageClass): TRasterImage`: creates an instance of the given image class and writes the currently visible view port image to it. | |
| 168 | + | - `procedure CenterOnObj(obj: TGPSObj)`: Centers the map on the provided GPS object. More on GPS objects below. | |
| 169 | + | - `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. | |
| 170 | + | - `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. | |
| 171 | + | ||
| 172 | + | ### Map Providers | |
| 173 | + | ||
| 174 | + | 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. | |
| 175 | + | ||
| 176 | + | Here is a list of the names of the built-in map providers: | |
| 177 | + | ||
| 178 | + | - OpenStreetMap Mapnik | |
| 179 | + | - OpenStreetMap Wikipedia | |
| 180 | + | - OpenStreetMap Sputnik | |
| 181 | + | - OpenStreetMap.fr Hot | |
| 182 | + | - OpenStreetMap.fr Cycle Map | |
| 183 | + | - Open Topo Map | |
| 184 | + | - Google Maps | |
| 185 | + | - Google Satellite | |
| 186 | + | - Yandex.Maps | |
| 187 | + | - Yandex.Maps Satellite | |
| 188 | + | - Virtual Earth Bing | |
| 189 | + | - Virtual Earth Aerial | |
| 190 | + | - Virtual Earth Hybrid | |
| 191 | + | ||
| 192 | + | 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. | |
| 193 | + | ||
| 194 | + | - Registration at <https://www.thunderforest.com/docs/apikeys/>, store API key in the variable `ThunderForest_ApiKey`: | |
| 195 | + | - Open Cycle Map | |
| 196 | + | - OpenStreetMap Transport | |
| 197 | + | - 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: | |
| 198 | + | - Here WeGo Map | |
| 199 | + | - Here WeGo Grey Map | |
| 200 | + | - Here WeGo Reduced Map | |
| 201 | + | - Here WeGo Transit Map | |
| 202 | + | - Here WeGo POI Map | |
| 203 | + | - Here WeGo Pedestrian Map | |
| 204 | + | - Here WeGo DreamWorks Map | |
| 205 | + | - Registration at <https://home.openweathermap.org/users/sign_up>, store the API Key in variable `OpenWeatherMap_ApiKey`: | |
| 206 | + | - OpenWeatherMap Clouds | |
| 207 | + | - OpenWeatherMap Precipitation | |
| 208 | + | - OpenWeatherMap Pressure | |
| 209 | + | - OpenWeatherMap Temperature | |
| 210 | + | - OpenWeatherMap Wind | |
| 211 | + | ||
| 212 | + | Please respect the license requirements of the map providers. | |
| 213 | + | ||
| 214 | + | ### GPS Objects | |
| 215 | + | ||
| 216 | + | The public `TMapView` property `GPSItems` collects data on points of interest and tracks to be overlayed on the map. The common data type of these overlay items is | |
| 217 | + | ||
| 218 | + | ``` Pascal | |
| 219 | + | type | |
| 220 | + | TGPSObj = class | |
| 221 | + | ... | |
| 222 | + | public | |
| 223 | + | destructor Destroy; override; | |
| 224 | + | property Name: String read FName write FName; | |
| 225 | + | property ExtraData: TObject read FExtraData write SetExtraData; | |
| 226 | + | property IdOwner: Integer read FIdOwner; | |
| 227 | + | property BoundingBox: TRealArea read GetBoundingBox write SetBoundingBox; | |
| 228 | + | end; | |
| 229 | + | ``` | |
| 230 | + | ||
| 231 | + | The `Name` can be displayed near the item in the map. | |
| 232 | + | ||
| 233 | + | `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`. | |
| 234 | + | ||
| 235 | + | `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. | |
| 236 | + | ||
| 237 | + | #### Points | |
| 238 | + | ||
| 239 | + | A special `TGPSObj` item is a `TGPSPoint`, for example a scenic "Point-of-Interest" (POI), a way point along a hiking path etc. | |
| 240 | + | ||
| 241 | + | ``` Pascal | |
| 242 | + | TGPSPoint = class(TGPSObj) | |
| 243 | + | public | |
| 244 | + | constructor Create(ALon,ALat: double; AEle: double=NO_ELE; ADateTime: TDateTime=NO_DATE); | |
| 245 | + | class function CreateFrom(aPt: TRealPoint): TGPSPoint; | |
| 246 | + | ||
| 247 | + | function HasEle: boolean; | |
| 248 | + | function HasDateTime: Boolean; | |
| 249 | + | function DistanceInKmFrom(OtherPt: TGPSPoint; UseEle: boolean=true): double; | |
| 250 | + | ||
| 251 | + | property Lon: Double read GetLon; | |
| 252 | + | property Lat: Double read GetLat; | |
| 253 | + | property Ele: double read FEle; | |
| 254 | + | property DateTime: TDateTime read FDateTime; | |
| 255 | + | property RealPoint: TRealPoint read FRealPt; | |
| 256 | + | end; | |
| 257 | + | ``` | |
| 258 | + | ||
| 259 | + | In its constructor, the geo coordinate longitude and latitude must be specified. They are also available as read-only properties `Lon` and `Lat`. Optional parameters are the elevation of the point (property `Ele` as well as some date/time information (property `DateTime`), for example the date when this point was visited. A special function, `DistanceInKmFrom`, is available to calculate the on-earth distance from another GPS point. | |
| 260 | + | ||
| 261 | + | #### Tracks | |
| 262 | + | ||
| 263 | + | The second important `TGPSObj` descendant is `TGPSTrack` which stores the points along a path, for example points visited on a trip or a hike. Each point is a instance of the `TGPSPoint` class, and the individual points are collected by the list `Points`. Again, there is a `DataTime` property usable for example to hold the date of the hike. Function `TrackLengthInKm` calculates the length of the entire path with the points being connected by straight line segments. | |
| 264 | + | ||
| 265 | + | ``` Pascal | |
| 266 | + | type | |
| 267 | + | TGPSTrack = class(TGPSObj) | |
| 268 | + | private | |
| 269 | + | FDateTime: TDateTime; | |
| 270 | + | FPoints: TGPSPointList; | |
| 271 | + | function GetDateTime: TDateTime; | |
| 272 | + | public | |
| 273 | + | constructor Create; | |
| 274 | + | destructor Destroy; override; | |
| 275 | + | ||
| 276 | + | procedure GetArea(out Area: TRealArea); override; | |
| 277 | + | function TrackLengthInKm(UseEle: Boolean=true): double; | |
| 278 | + | ||
| 279 | + | property Points: TGPSPointList read FPoints; | |
| 280 | + | property DateTime: TDateTime read GetDateTime write FDateTime; | |
| 281 | + | end; | |
| 282 | + | ``` | |
| 283 | + | ||
| 284 | + | #### List of GPS Objects | |
| 285 | + | ||
| 286 | + | The property `GpsItems` of the `TMapView` component is the main class collecting all the GPS objects. It is an instance of the `TGPSObjectList`: | |
| 287 | + | ||
| 288 | + | ``` Pascal | |
| 289 | + | type | |
| 290 | + | TGPSObjectList = class(TGPSObj) | |
| 291 | + | ... | |
| 292 | + | constructor Create; | |
| 293 | + | destructor Destroy; override; | |
| 294 | + | ||
| 295 | + | Procedure Clear(OwnedBy: integer); | |
| 296 | + | procedure ClearExcept(OwnedBy: integer; const ExceptLst: TIdArray; out Notfound: TIdArray); | |
| 297 | + | procedure GetArea(out Area: TRealArea); override; | |
| 298 | + | function GetObjectsInArea(const Area: TRealArea): TGPSObjList; | |
| 299 | + | function GetIdsArea(const Ids: TIdArray; AIdOwner: integer): TRealArea; | |
| 300 | + | ||
| 301 | + | function Add(aItem: TGpsObj; AIdOwner: integer): integer; | |
| 302 | + | procedure DeleteById(const Ids: Array of integer); | |
| 303 | + | ||
| 304 | + | procedure BeginUpdate; | |
| 305 | + | procedure EndUpdate; | |
| 306 | + | ||
| 307 | + | property Count: integer read GetCount; | |
| 308 | + | property Items[AIndex: Integer]: TGpsObj read GetItem; default; | |
| 309 | + | ||
| 310 | + | property OnModified: TModifiedEvent read FOnModified write FOnModified; | |
| 311 | + | end; | |
| 312 | + | ``` | |
| 313 | + | ||
| 314 | + | 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 assigned to an arbitrary ID which is used to group the objects. | |
| 315 | + | ||
| 316 | + | 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. | |
| 317 | + | ||
| 318 | + | `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). | |
| 319 | + | ||
| 320 | + | #### Example: Adding a Point of Interest | |
| 321 | + | ||
| 322 | + | 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`: | |
| 323 | + | ||
| 324 | + | ``` Pascal | |
| 325 | + | uses | |
| 326 | + | mvTypes, mvGPSObj; | |
| 327 | + | ||
| 328 | + | const | |
| 329 | + | _POI_ = 10; | |
| 330 | + | ||
| 331 | + | procedure TForm1.MapView1MouseUp(Sender: TObject; Button: TMouseButton; | |
| 332 | + | Shift: TShiftState; X, Y: Integer); | |
| 333 | + | var | |
| 334 | + | pt: TRealPoint; | |
| 335 | + | gpsPt: TGpsPoint; | |
| 336 | + | ptName: String; | |
| 337 | + | begin | |
| 338 | + | ptName := ''; | |
| 339 | + | if InputQuery('Point of Interest', 'Enter name:', ptName) then | |
| 340 | + | begin | |
| 341 | + | pt := MapView1.ScreenToLonLat(Point(X, Y)); | |
| 342 | + | gpsPt := TGpsPoint.CreateFrom(pt, NO_ELE, Now() ); | |
| 343 | + | gpsPt.Name := ptName; | |
| 344 | + | Mapview1.GPSItems.Add(gpsPt, _POI_); | |
| 345 | + | end; | |
| 346 | + | end; | |
| 347 | + | ``` | |
| 348 | + | ||
| 349 | + | 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 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). | |
| 350 | + | ||
| 351 | + | There is also an event, `OnDrawGpsPoint`, which can be used to replace the entire labeling process by custom routines. The event has the `GpsPoint` as parameter. Drawing style can be defined based on the data stored in the `GpsPoint`. The other event parameter is the `DrawingEngine` which provides methods for drawing; it will be explained below. Here is an example in which the added Point-of-Interest is drawn by a blue circle and labeled by a large bold font: | |
| 352 | + | ||
| 353 | + | ``` Pascal | |
| 354 | + | procedure TForm1.MapView1DrawGpsPoint(Sender: TObject; | |
| 355 | + | ADrawer: TMvCustomDrawingEngine; APoint: TGpsPoint); | |
| 356 | + | const | |
| 357 | + | R = 8; | |
| 358 | + | var | |
| 359 | + | p: TPoint; | |
| 360 | + | begin | |
| 361 | + | // Screen coordinates of the GPS point | |
| 362 | + | p := MapView1.LonLatToScreen(APoint.RealPoint); | |
| 363 | + | ||
| 364 | + | // Draw a blue circle | |
| 365 | + | ADrawer.PenColor := clBlack; | |
| 366 | + | ADrawer.BrushColor := clBlue; | |
| 367 | + | ADrawer.BrushStyle := bsSolid; | |
| 368 | + | ADrawer.Ellipse(p.X-R, p.Y-R, P.X+R, p.Y+R); | |
| 369 | + | ||
| 370 | + | // Draw the point label | |
| 371 | + | ADrawer.BrushStyle := bsClear; | |
| 372 | + | ADrawer.FontName := 'SegoeUI'; | |
| 373 | + | ADrawer.FontSize := 16; | |
| 374 | + | ADrawer.FontStyle := [fsBold]; | |
| 375 | + | ADrawer.TextOut( | |
| 376 | + | p.X - ADrawer.TextWidth(APoint.Name) div 2, | |
| 377 | + | p.Y + R + 4, | |
| 378 | + | APoint.Name | |
| 379 | + | ); | |
| 380 | + | end; | |
| 381 | + | ``` | |
| 382 | + | ||
| 383 | + | ## Finding Geo Locations: TMvGeoNames | |
| 384 | + | ||
| 385 | + | `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. | |
| 386 | + | ||
| 387 | + | <figure> | |
| 388 | + | <img src="tmvgeonames_200.png" title="tmvgeonames_200.png" /> | |
| 389 | + | <figcaption>tmvgeonames_200.png</figcaption> | |
| 390 | + | </figure> | |
| 391 | + | ||
| 392 | + | ### Getting Started | |
| 393 | + | ||
| 394 | + | - 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. | |
| 395 | + | - Add a button which is supposed to trigger the search. | |
| 396 | + | - 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: | |
| 397 | + | ||
| 398 | + | ``` pascal | |
| 399 | + | procedure TForm1.Button1Click(Sender: TObject); | |
| 400 | + | begin | |
| 401 | + | MapView1.Center := MvGeoNames1.Search('Grand Canyon National Park', MapView1.DownloadEngine); | |
| 402 | + | end; | |
| 403 | + | ``` | |
| 404 | + | ||
| 405 | + | ### Documentation | |
| 406 | + | ||
| 407 | + | #### Methods | |
| 408 | + | ||
| 409 | + | - `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. | |
| 410 | + | ||
| 411 | + | #### Properties | |
| 412 | + | ||
| 413 | + | - `LocationName: string`: Is the name of the (first) found location as listed by the *geonames* site. | |
| 414 | + | ||
| 415 | + | #### Events | |
| 416 | + | ||
| 417 | + | - `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: | |
| 418 | + | ||
| 419 | + | ``` pascal | |
| 420 | + | type | |
| 421 | + | TGeoLocation = class | |
| 422 | + | Name: String; | |
| 423 | + | Descr: String; | |
| 424 | + | Coords: TRealPoint; | |
| 425 | + | end; | |
| 426 | + | ||
| 427 | + | procedure TForm1.MVGeoNames1NameFound(const AName: string; | |
| 428 | + | const ADescr: String; const ALoc: TRealPoint); | |
| 429 | + | var | |
| 430 | + | loc: TGeoLocation; | |
| 431 | + | begin | |
| 432 | + | loc := TGeoLocation.Create; | |
| 433 | + | loc.Name := AName; | |
| 434 | + | loc.Descr := ADescr; | |
| 435 | + | loc.Coords := ALoc; | |
| 436 | + | ComboBox1.Items.AddObject(AName, loc); | |
| 437 | + | end; | |
| 438 | + | ``` | |
| 439 | + | ||
| 440 | + | --- | |
| 441 | + | *From the Free Pascal wiki, page "LazMapViewer", as it stood on 2024-01-29 after a change by Wp, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's [[Licensing]] page. If you have concerns about this material, [message the admin](/wiki-contact).* |
