A Racket GUI Widget to display maps based on Open  Street  Map tiles
1 Map Widget Reference
map-widget%
new
zoom-level
show-map-layer
center-map
move-to
resize-to-fit
auto-resize-to-fit
begin-edit-sequence
end-edit-sequence
add-layer
remove-layer
map-snip%
new
zoom-level
show-map-layer
center-map
move-to
resize-to-fit
auto-resize-to-fit
begin-edit-sequence
end-edit-sequence
add-layer
remove-layer
1.1 Layers
layer<%>
get-name
get-zorder
set-zorder
lines-layer%
set-pen
line-layer
lines-layer
markers-layer%
markers-layer
points-layer%
points-layer
point-cloud-layer%
get-point-count
clear
add-points
set-color-map
point-cloud-layer
current-location-layer%
current-location
track-current-location
current-location-layer
1.2 Tile Providers
get-tile-providers
current-tile-provider
set-current-tile-provider
8.12

A Racket GUI Widget to display maps based on OpenStreetMap tiles 🔗ℹ

Alex Harsányi

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

class

map-widget% : class?

  superclass: object%

A widget to display map plus GPS tracks and markers.

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.

method

(send a-map-widget center-map group)  any/c

  group : (or/c #f integer? symbol?)
Center the map around the specified track group, or around all tracks if group is #f

method

(send a-map-widget move-to position)  any/c

  position : (vector/c flonum? flonum?)
Center 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

(send a-map-widget add-layer layer)  any/c

  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.

class

map-snip% : class?

  superclass: snip%

A snip% to display map plus GPS tracks and markers, which can be inserted in a pasteboard%.

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.

method

(send a-map-snip center-map group)  any/c

  group : (or/c #f integer? symbol?)
Center the map around the specified track group, or around all tracks if group is #f

method

(send a-map-snip move-to position)  any/c

  position : (vector/c flonum? flonum?)
Center 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

(send a-map-snip add-layer layer)  any/c

  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🔗ℹ

interface

layer<%> : interface?

Common interface provided by all map layers, layers are used to show lines, points, markers and point-clouds on the map.

method

(send a-layer get-name)  (or/c symbol? string?)

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.

class

lines-layer% : class?

  superclass: object%

  extends: layer<%>
A map layer that draws a sequence of waypoints as lines. Use lines-layer and line-layer to create line layers.

method

(send a-lines-layer set-pen p)  any/c

  p : (is-a/c pen%)
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
Creates a lines-layer% object named name from waypoints which is a sequence of latitude and longitude coordinates.

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
Same as line-layer, but the layer each waypoint sequence in tracks will be drawn as a separate line. This can be used to create a single layer containing several and possibly disconnected lines.

class

markers-layer% : class?

  superclass: object%

  extends: layer<%>
A layer that can be used to display one or more labeled markers on the map. Use markers-layer to create marker layers.

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
Create a markers-layer% named name from the list of markers. Each marker is specified by a list of 4 elements: a latitude/longitude coordinate, the label to be shown on the map, the location of the label -1 to the left, 1 to the right and a color, specified either as a color name or a color% object.

class

points-layer% : class?

  superclass: object%

  extends: layer<%>
Create a map layer which draws individual points. Individual points can be highlighted when the user moves the mouse over them and a tooltip, represented as a pict can be displayed for each point. Use points-layer to create a points layer.

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
Create a a points-layer% named name from a list of points which are latitude/longitude positions.

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.

class

point-cloud-layer% : class?

  superclass: object%

  extends: layer<%>
A point-cloud layer shows a large amount (millions) of points on a map, grouped together and colored using a color map to show density of data around a location.

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).

method

(send a-point-cloud-layer clear)  any/c

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
Create a new point cloud layer with a default color map.

class

current-location-layer% : class?

  superclass: object%

  extends: layer<%>
A map layer that displays a single location on the map, marked by a circle. The location can be updated by the user, and can be used to represent a way to associate some other data with a location on the map. For example, an application might show an elevation plot and, when the user hovers over the plot, the location over the plot is shown on the map using this layer type.

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
Create a new current-location-layer% with the specified name and drawing parameters.

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?)

Returns a list of available tile providers.

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.

Return the name of the current tile provider.

procedure

(set-current-tile-provider name)  any/c

  name : string?
Set the name of the tile provider to use. All instances of map-widget% will use the same tile provider.

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.