A library for interacting with online map services (Bing Maps, Google Maps, OpenStreetMap and OpenTopoMap are currently supported), plus WinUI 3 controls for displaying maps in WinUI 3 apps.
Introduction
I've written several Windows applications that involve displaying maps obtained from online services. Unfortunately, to my knowledge, there is no "official" Microsoft (or Microsoft community) control for displaying maps under WinUI 3. There are some available via GitHub, but I thought it would be an interesting project to develop my own.
The source code is available on GitHub.
Packages are available on Nuget.org:
Background
Like many others, I've used maps on many websites and in a number of desktop applications. But I wasn't familiar with the mechanics of how the various mapping services worked, which, completely aside from learning how to program them, was fascinating in its own right. A great place to start is with The Bing Maps Tile System which, in the course of explaining how their tile system works, will introduce you to why map services use tile-based systems and some of the challenges of using them in code libraries.
Using the Code
There is extensive documentation available for both libraries at the GitHub repository. The repository also contains a WinUI 3 demonstration program (which, admittedly, uses some of my other libraries, all of which are available from NuGet.org) and a set of unit tests, which also show how the J4JMapLibrary
can be used.
J4JMapLibrary
The J4JMapLibrary
uses the concept of a "map region" (embodied in a MapRegion
class), "map tiles" (embodied in a MapTile
class) and a "projection" (embodied in a class derived from Projection
, customized for dealing with a particular mapping service). The library currently has projection classes for Bing Maps, Google Maps, OpenStreetMap and OpenTopoMap. It shouldn't be too hard write your own projection class for another mapping service, either.
MapTiles
are always part of a MapRegion
, which defines the area of the map you want to display, its heading (i.e., how it may be rotated relative to true north) and its scale (i.e., how zoomed in it is). A Projection
loads image data into a MapTile
, or all the tiles contained in a MapRegion
, which can then be used to update the UI. MapRegion
contains various properties which make that updating easier (e.g., the horizontal and vertical offset of the display area relative to the area retrieved from the mapping service, which can differ).
While instantiating a Projection
is straightforward, the library also contains a MapFactory
which can be used to create Projections
based on their name or type. MapFactory
can also automatically authenticate a Projection
(all of the mapping services require some degree of authentication to use them, and some also require you to set up accounts...which can incur fees if you use the service a lot). For MapFactory
to do authentications, your credentials must be available through the IConfiguration
system, and follow a particular layout. Details are contained in the GitHub documentation.
Some Projections support caching the retrieved image data (it is a violation of the Google Maps Static API to cache their image data). The library contains both an in-memory and a file system-based cache. Caching is not required but is recommended.
J4JMapWinLibrary
In and of itself, J4JMapLibrary
doesn't actually display anything. And, because maps are inherently visual, the library will likely be of limited use in the command line environment.
J4JMapWinLibrary
contains controls for displaying maps, and annotations, in a WinUI 3 application. It currently contains two controls, J4JMapControl
(which displays maps) and MapPin
(which can be used to display annotations "above" the map in a J4JMapControl
).
Using J4JMapControl
is simple: just add it to your app and assign certain properties to it.
<map:J4JMapControl x:Name="mapControl"
Grid.Column="0" Grid.Row="0" Grid.ColumnSpan="3"
MapProjection="BingMaps"
MapScale="13"
Heading="45"
Center="37.5072N,122.2605W">
J4JMapControl
has a reasonably long list of properties which you can use to tailor it to your needs. It offers the ability to display a compass rose (which shows the map's heading relative to true north) and a map scale slider control (which controls the map scale/zoom factor).
It also responds to click-and-drag actions. By default, those cause the center of the map to move to follow the mouse. But if you hold down the control key, the map rotates (i.e., its heading changes) in response to the mouse movements.
J4JMapControl
will also respond to mouse wheel movements by changing the map scale/zoom factor.
The library also contains a MapPin
control, which you can use to annotate locations on the map. Adding an annotation is simple in XAML:
<map:J4JMapControl x:Name="mapControl"
Grid.Column="0" Grid.Row="0" Grid.ColumnSpan="3"
MapProjection="BingMaps"
MapScale="13"
Heading="45"
Center="37.5072N,122.2605W">
<map:J4JMapControl.Annotations>
<map:MapPin ArcRadius="15"
TailLength="30"
map:Location.Center="37.5072N,122.2605W"
HorizontalAlignment="Center"
VerticalAlignment="Bottom" />
</map:J4JMapControl.Annotations>
</map:J4JMapControl>
Technically, any FrameworkElement
can be used as an annotation, provided you assign it a Location
property (i.e., the map:Location.Center
reference in the code block above). Be aware, though, that the library currently doesn't "know" how to handle complex objects, so they likely won't be display properly, or at all.
Next Steps
The libraries are essentially complete at this point. There are a few minor bugs to resolve, and I also want to provide a mechanism for obtaining and storing the credentials required to access the supported map services.
Points of Interest
Developing these libraries taught me I knew almost nothing at all about online maps. I also learned that the geometry/mathematics of handling maps is insanely complicated compared to what I expected. There are multiple frames of reference you have to keep straight, and, since they all use the same underlying types, it's easy to get lost as to which one you're dealing with or how it must be converted to comply with the requirements of some other frame of reference.
History
- 6th April, 2023: Initial public release (0.8)
- 13th April, 2023: Bug fix, minor capability enhancements (0.8.3)
- 25th April, 2023: Added
DataTemplate
based annotations (0.8.4) - 26th April, 2023: Added support for displaying routes (0.9)