A Racket GUI Widget to display maps based on OpenStreetMap tiles
This module contains a widget to display a map based on OpenStreetMap data, plus additional markers and GPS tracks. The widget allows zooming and paning with the mouse, as well as programatically. Two interfaces are available: map-widget% is based on a canvas% implementation, and can be added to other GUI elements, while map-snip% implements the map as a snip% object which can be inserted into a pasteboard%.
The package was initially written to support the data visualisation needs for ActivityLog2. That application uses all the features implemented by this package, and shows many complex usage scenarios of the map widget.
1 Map Widget Reference
(require map-widget) | package: map-widget |
|
superclass: object% |
constructor
(new map-widget% [parent parent] [ [position position]]) → (is-a?/c map-widget%)
parent :
(or/c (is-a?/c frame%) (is-a?/c dialog%) (is-a?/c panel%) (is-a?/c pane%)) position : (or/c #f (vector/c flonum? flonum?)) = #f Construct a new widget. position specifies the initial position shown in the center of the map, it is a vector containing the latitude and longitude. If position is #f, a defatult position will be used.
method
(send a-map-widget zoom-level level) → any/c
level : exact-nonnegative-integer? (send a-map-widget zoom-level) → exact-nonnegative-integer? Set or get the zoom level for the displayed map. The zoom level is a value between 1 (zoomed out) and 16 (zoomed in). The zoom level can also be changed by the user using the mousewheel, so once set, it is not guaranteed to remain at the same value.
method
(send a-map-widget show-map-layer show?) → any/c
show? : boolean? (send a-map-widget show-map-layer) → boolean? Set or get whether to show the map tile layer not not. If the map tile layer is hidden, only the traks and markers will be visible.Center the map around the specified track group, or around all tracks if group is #fCenter the map around the specified position, a vector containing the latitude and longitude.
method
(send a-map-widget resize-to-fit group) → any/c
group : (or/c #f symbol?) Center and set the zoom level of the map such that the specified track group fits on the visible part of the map canvas. If group is #f, all the tracks will fit on the map canvas.
method
(send a-map-widget auto-resize-to-fit flag) → any/c
flag : boolean? (send a-map-widget auto-resize-to-fit) → boolean? Set a flag whether the map should be automatically resized to fit after adding a track, point cloud or marker. This can be used in interactive programs which add several tracks in sequence.The flag will be automatically cleared when the user moves the map or zooms it.
method
(send a-map-widget begin-edit-sequence) → any/c
(send a-map-widget end-edit-sequence) → any/c These two methods can be used to group together several operations on the map widget, avoiding intermediate refresh calls which might cause flickering.The begin-edit-sequence and end-edit-sequence calls can be nested.
method
layer : (is-a/c layer<%>) (send a-map-widget remove-layer layer-name) → any/c layer-name : (or/c symbol? integer?) Add or remove a layer from the map, layers contain tracks (lists of waypoints), individual points, named locations or point clouds.
constructor
(new map-snip% [ [position position] [track track] [width width] [height height]]) → (is-a?/c map-snip%) position : (or/c #f (vector/c flonum? flonum?)) = #f track : sequence? = #f width : positive? = 600 height : positive? = 300 Construct a new map snip. position specifies the initial position shown in the center of the map, it is a vector containing the latitude and longitude. If position is #f, a defatult position will be used.track specifies an initial track to add to the map, if #f, no tracks will be added.
width and height represent the initial dimensions for the snip%.
method
(send a-map-snip zoom-level level) → any/c
level : exact-nonnegative-integer? (send a-map-snip zoom-level) → exact-nonnegative-integer? Set or get the zoom level for the displayed map. The zoom level is a value between 1 (zoomed out) and 16 (zoomed in). The zoom level can also be changed by the user using the mousewheel, so once set, it is not guaranteed to remain at the same value.
method
(send a-map-snip show-map-layer show?) → any/c
show? : boolean? (send a-map-snip show-map-layer) → boolean? Set or get whether to show the map tile layer not not. If the map tile layer is hidden, only the traks and markers will be visible.Center the map around the specified track group, or around all tracks if group is #fCenter the map around the specified position, a vector containing the latitude and longitude.
method
(send a-map-snip resize-to-fit group) → any/c
group : (or/c #f symbol?) Center and set the zoom level of the map such that the specified track group fits on the visible part of the map canvas. If group is #f, all the tracks will fit on the map canvas.
method
(send a-map-snip auto-resize-to-fit flag) → any/c
flag : boolean? (send a-map-snip auto-resize-to-fit) → boolean? Set a flag whether the map should be automatically resized to fit after adding a track, point cloud or marker. This can be used in interactive programs which add several tracks in sequence.The flag will be automatically cleared when the user moves the map or zooms it.
method
(send a-map-snip begin-edit-sequence) → any/c
(send a-map-snip end-edit-sequence) → any/c These two methods can be used to group together several operations on the map widget, avoiding intermediate refresh calls which might cause flickering.The begin-edit-sequence and end-edit-sequence calls can be nested.
method
layer : (is-a/c layer<%>) (send a-map-snip remove-layer layer-name) → any/c layer-name : (or/c symbol? integer?) Add or remove a layer from the map, layers contain tracks (lists of waypoints), individual points, named locations or point clouds.
1.1 Layers
|
Return the name of this layer.
method
(send a-layer get-zorder) → (between/c 0 1)
(send a-layer set-zorder zorder) → any/c zorder : (between/c 0 1) Set or get the drawing order of this layer. Layers with lower z-order values are drawn earlier, and this are "below" layers with higher z-order values.
Set the pen used to draw lines in this layer.
procedure
(line-layer name waypoints [ #:pen pen #:zorder zorder]) → (is-a/c lines-layer%) name : (or/c symbol? integer?) waypoints : (sequence/c (vector/c real? real?)) pen : (is-a/c pen%) = 'default-line-pen zorder : (between/c 0 1) = 0.2
procedure
(lines-layer name tracks [ #:pen pen #:zorder zorder]) → (is-a/c lines-layer%) name : (or/c symbol? integer?) tracks : (listof (sequence/c (vector/c real? real?))) pen : (is-a/c pen%) = 'default-line-pen zorder : (between/c 0 1) = 0.5
procedure
(markers-layer name markers [#:zorder zorder])
→ (is-a/c markers-layer%) name : (or/c symbol? integer?)
markers :
(listof (list/c (vector/c real? real?) string? (or/c -1 1) (or/c string? (is-a/c color%)))) zorder : (between/c 0 1) = 0.5
procedure
(points-layer name points [ #:zorder zorder #:pen pen #:brush brush #:size size #:hlpen hlpen #:hlbrush hlbrush #:hlsize hlsize #:hover-callback hover-callback]) → (is-a/c points-layer%) name : (or/c symbol? integer?) points : (lisfof (vector/c real? real?)) zorder : (between/c 0 1) = 0.3 pen : (is-a/c pen%) = 'default-points-pen brush : (is-a/c brush%) = 'default-points-brush size : (>/c 0) = 10 hlpen : (is-a/c pen%) = 'default-points-hlpen hlbrush : (is-a/c brush%) = 'default-points-hlbrush hlsize : (>/c 0) = 15
hover-callback : (-> exact-nonnegative-integer? (or/c #f pict?)) = 'default-hover-callback
Each point is drawn asa a disk (circle) using pen and brush and it is of the specified size. When the user moves the mouse over a point, it is highlighted, that is, drawn using hlpen and hlbrush and hlsize.
hover-callback represends an optional callback, whcih is invoked with the index of the point that is highlighted. It can return either #f, in which case nothing is displayed, or a pict object which is displayed next to the point – this can be used to display additional information about the point. The default callback always returns #f.
method
(send a-point-cloud-layer get-point-count) →
integer? integer? Return the number of points in the point cloud as two values: the number of points that have been processed and available for drawing, and the total number of points that were added to the point cloud, this last value includes points that are not yet processed, since point processing happens in a separate OS thread (place).Clear all the points in the point cloud.
method
(send a-point-cloud-layer add-points points #:format fmt) → any/c
points :
(or/c (listof? integer?) (listof? (list/c real? real?)) (listof? (vector/c real? real?))) fmt : (or/c 'lat-lng 'geoids 'ordered-geoids) Add some GPS points to the point cloud. The points can be specified either as latitude/longitude pairs or as geoids (ordered or not). This method can be called multiple times, allowing for streaminh in data points.fmt specifies the format of input data: 'lat-lng means the data is a sequence of latitude/longitude pairs, 'geoids means the data is a list of geoids (possibly unordered), while 'ordered-geoids specifies that the data is an ordered list of geoids.
For large amonts of data, ordered geoids are the fastest to process, but it is only worthwhile using it if the data is already ordered (e.g stored as such in a database). If you only have latitude/longitude pairs, converting them to geoids and sorting them will not make it faster.
For mode information on geoids, see the (part ("(lib geoid/main.rkt)" "top")) package.
method
(send a-point-cloud-layer set-color-map cm) → any/c
cm :
(listof? (or/c (list real? real? real?) string? (is-a?/c color%))) Set the color map used for rendering point clouds, the color map is a list of colors, the first will be used for the least amount of points in a location, while the last color for coloring locations with most points. In-between colors will be used for locations of intermediate number of points.
procedure
(point-cloud-layer name [ #:zorder zorder #:color-map color-map]) → (is-a/c point-cloud-layer%) name : (or/c symbol? integer?) zorder : (between/c 0 1) = 0.4 color-map : (or/c #f (listof (list/c real? real? real?))) = #f
method
(send a-current-location-layer current-location location)
→ any/c location : (or/c (vector/c real? real?) #f) Set the location to be shown on the map, or clears it #f is used as the location.
method
(send a-current-location-layer track-current-location flag)
→ any/c flag : boolean? If flag is #t, the map will be automatically panned so that the location speficied by set-current-location is always in the middle of the map, when flag is #f, this functionality is disabled
procedure
(current-location-layer name [ #:track-current-location? track-current-location? #:zorder zorder #:pen pen #:brush brush #:size size]) → (is-a/c current-location-layer%) name : (or/c symbol? string) track-current-location? : boolean? = #f zorder : (between/c 0 1) = 0.6 pen : (is-a/c pen%) = 'default-current-location-pen brush : (is-a/c brush%) = 'default-current-location-brush size : (>/c 0) = 24
1.2 Tile Providers
The map widget displays a map as a collection of tiles, each tile is a square bitmap 256 pixels in width and height. These tiles are downloaded from tile servers as needed and they are stored locally in a persistent cache on disk. The Open Street Map tile server is always available, and it is used by the map widget by default. In addition tiles from the Thunderforest service can also be used, but this requires an API key to be set. If you have an API key, and you can get a developer one for free, set the AL2TFAPIKEY environment variable to contain the API key. The API key is compiled into the code and the environment variable is not needed if you distribute a built application using the map widget.
You can set the tile provider to use by calling set-current-tile-provider and you can get a list of available tile providers by calling get-tile-provider-names.
procedure
(get-tile-providers) → (listof string?)
By default only the Open Street Map tile server is available. To use Thunderforest tiles, you will need to obtain an API key and set the AL2TFAPIKEY environment variable with that API key.
procedure
procedure
(set-current-tile-provider name) → any/c
name : string?
When this function is called, the selected tile provider is also stored int the racket preference file and this tile provider will be used each time the application is run, util this function is called with a different tile provider name.