Map

Map plots Dataverse rows from a view or query at their latitude and longitude, beside markers, lines, areas and circles of your own. One component, the same on Blazor and React, drawn by the provider you choose: Azure Maps, Esri, Google, OpenStreetMap or any MapLibre style.

Offices, a route and areas

Twelve offices as markers, a route, a delivery zone and a sales region. Markers close together gather into clusters, which zoom in when clicked. Click an office for its popup, here the page's own content; the route and the areas open none, as the page answers their clicks itself, and the line under the map shows what it heard.

React example Blazor example React TypeScript Razor
Loading the map…

Last clicked: Nothing yet

Accounts and contacts from Dataverse

The active accounts, colored by their relationship type, or with the switch the contacts, read by a FetchXML query with the visitor's permissions and placed at their latitude and longitude. Click one for its popup: the query's other columns under the row's name, which opens its page. The box over the map finds an address with the portal's provider and pins it.

React example Blazor example React TypeScript Razor
  • Customer
  • Investor
  • Partner
  • Prospect
  • Reseller
  • Vendor
Loading the map…

Last clicked: Nothing yet
Address found: Search for one in the box over the map

Providers

The portal's configuration names its provider and holds each provider's keys, in the PowerPortalsPro:Maps section, so a map needs to name none and no page holds a key. Markers, clusters, popups and clicks work the same on every provider:

  • Azure Maps: no key at all, as the portal signs in with its own identity (below). Its attribution is shown as its terms ask.
  • Esri: an ArcGIS API key in Esri:ApiKey, and any ArcGIS basemap style in place of streets.
  • Google: a Maps JavaScript API key in Google:ApiKey, and a map id for a map styled in Google Cloud.
  • OpenStreetMap: no key, and the provider when none is set, for development and light use. OpenStreetMap's tile policy rules out heavy use; name your own tile server in OpenStreetMap:TileUrl for production.
  • MapLibre: any MapLibre style or raster tile URL in MapLibre:StyleUrl or MapLibre:TileUrl, such as MapTiler, OpenFreeMap, Stadia or your own server.

Esri's and Google's keys reach the viewer's browser, which sends them to the provider, as every web map's do. Restrict each to your portal's addresses in the provider's console.

A page names a provider only to use another than the portal's, and says only how it looks: a style, a map id, a view of disputed borders. What it leaves unset comes from the configuration. In a dark theme the map switches to the provider's dark style; UsePortalTheme turns that off.

React Blazor

Azure Maps without a key

An Azure Maps map signs in with Entra tokens the portal issues to its own identity, the one it connects to Dataverse as, so no Azure Maps key is needed or reaches the browser. Give that identity the Azure Maps Data Reader role on the Azure Maps account and nothing more, and set AzureMaps:ClientId to the client id on the account's Authentication page.

Every viewer holds the token while it runs, so it can do only what that role allows; limit the account's allowed origins to your portal's addresses too. The map asks for a new token before it expires. A page asks once, and one viewer may be handed 120 tokens a minute unless PowerPortalsPro:Maps:RateLimit says otherwise; visitors who aren't signed in are counted by their address, which everyone behind one office's connection shares.

Dataverse rows

Give a view or a FetchXML query, and each row with a latitude and longitude becomes a marker named by the table's primary name. Rows are read with the user's permissions, as a grid's are, and clicking one shows its view's columns.

React Blazor

Rows without both coordinates are left out of the query. The map reads 1,000 rows unless MaxRows says otherwise (at most 5,000) and notes on the map when there are more.

Rows without coordinates can have them filled in from their addresses as they are saved: see Geocoding.

Several tables

With MultiTable (React: multiTable), the map shows the rows of every table the server registers for maps with ForMaps, each table in its own color with a legend. The registration names the columns holding a row's latitude and longitude, which are always read. DefaultTables chooses the tables shown at first and Tables the ones it may show at all; Regarding narrows it to the rows about a record.

A table whose coordinates are registered with AddMapCoordinates, or filled in by AddGeocoding, is registered for maps with ForMaps() alone: see Geocoding.

React Blazor

A toolbar over the map chooses the view: every table, or a view of several tables registered with AddMultiTableView and ForMaps(). ViewIds (React: viewIds) offers only the registered views it names, in its order, and DefaultViewId (defaultViewId) the one it opens on. Its Filter menu switches tables on and off and edits each table's filters. With AllowSavedViews (React: allowSavedViews), a signed-in user can save what the map shows as a view of their own: see Saved Views.

Finding records near a place

With AllowNearSearch (React: allowNearSearch), a map of several tables has a Near button on its toolbar. It finds a place — an address, with the map provider's own search, or the visitor's location, which the browser asks them to share — and the map then shows only the rows within the distance chosen, nearest first over every table: the circle is drawn, the map fitted to it, and each row's popup says how far it is. NearDistances (nearDistances) sets the distances on offer, 1, 5, 10, 25, 50 and 100 unless set, NearDistance (nearDistance) the one it starts at, 25 unless set, and NearDistanceUnit (nearDistanceUnit) whether it starts in miles, the default, or kilometers; the visitor can switch. See it on the Nearby demo.

React Blazor

Each table is read by its registered coordinates, with the visitor's permissions: the server reads the square around the circle, keeps the rows within the distance by their distance over the ground, and returns the nearest. A search near a place keeps 500 rows at most, the nearest, and the map's legend says when there were more; NearSearchOptions.MaximumMatches raises or lowers that, up to 5,000. A visitor who may not read a table's coordinates finds none of its rows.

Popups and clicks

Clicking an item, a marker or a line, area or circle alike, raises OnItemClick with the item, its data and where it was clicked, and then opens a popup over it. The popup is a Fluent card with your own content: a template on Blazor, a render function on React. A click on the map itself raises OnMapClick with its position.

React Blazor

To answer a click your own way, open a record's page, say, or select a region, call PreventDefault() on the click (preventDefault() on React) and the map opens no popup for it. ShowPopups set to false turns popups off for every item.

React Blazor

A row's popup heading links to its record's page, the one registered for its table with AddRecordPage (see Record Pages), on a map of one table or several. RecordUrl links it elsewhere: a URL with {0} in place of the row's id and {1} its table's. ShowRecordLinks (React: showRecordLinks) set to false turns the links off. An item of your own links to its Url. On React, onNavigate follows the link with your router.

Markers at one place, such as the contacts at one address, never come apart by zooming, so their cluster lists them instead.

Colors by a column

ColorColumn colors each row's marker by its option in a choice, status, status reason or yes/no column: the color the option has in Dataverse, or one of Fluent's palette when it has none, with a legend of the options on the map. Rows with no value are gray. For any other rule, RecordColorSelector on Blazor and a function for recordColor on React give each row its own color.

React Blazor

ShowAddressSearch puts a search box over the map's corner. It finds addresses and places with the portal's own provider, places in view first, and pins the one chosen; OnAddressSelected hands the page the place, and null once the search is cleared. AddressSearchCountries keeps it to some countries, and GoToAddressAsync (goToAddress on React's ref) searches from code.

React Blazor

Azure Maps, Esri and Google suggest places as the visitor types. Google's key needs the Places API (New) enabled, and Esri's key its geocoding privilege. OpenStreetMap and MapLibre maps search OpenStreetMap's Nominatim, which its usage policy allows only for light use and never as someone types, so they search when Enter is pressed; name your own Nominatim server in OpenStreetMap:GeocoderUrl or MapLibre:GeocoderUrl for more.

Address (address in React) shows one address without a search box: the map finds it with the same search once it is drawn, and again whenever it changes, pins it and zooms to it. A spinner shows while it looks, and a message when it isn't found. It is what AddressDisplayValue's map pin opens for an address with no coordinates.

React Blazor

Printing

ShowPrintButton (showPrintButton in React) adds a Print button — on the toolbar of a MultiTable map, else over the map's top right corner — that prints the map as it shows, with its pins, its legend, the popup open on it and the provider's attribution. PrintTitle is the heading above it, and PrintEntries lists records under it, each beside its pin's label. A marker's Label is drawn on its pin in place of the dot: give stops a number and the printout reads as a route sheet. PrintAsync() (print() on the React ref) prints from a button of the page's own, and OnPrintChanged (onPrintChange) says when the map can be printed and whether it can be saved as an image, for that button to follow.

React Blazor

Azure Maps, Esri, OpenStreetMap and MapLibre maps are printed as an image of the map, so the page around the map is left out and the map doesn't move; the same button also saves that image as a PNG file (SaveImageAsync, or saveImage()). An open popup is drawn on the image with its title and fields, or the markers it lists; one showing the page's own content (PopupTemplate, or renderPopup) is drawn with its item's title. A Google map can't be copied into an image, so it is printed as it is on the page, popup and all, the rest of the page left out of the printout, and offers no image to save. A provider of your own is printed as an image when its surface implements capture.

The provider's own features

NativeOptions are passed to the provider's own map as it is made, over the map's own. That turns on Street View for Google, for example, or lets a MapLibre map zoom with the scroll wheel alone (cooperativeGestures: false); by default it needs Ctrl, as Google's does on a page that scrolls, so the page scrolls past it. The provider's map object is also available to your own JavaScript: GetNativeMapAsync on Blazor, native on the React ref.

React Blazor

Your own provider

A provider draws the base map and shapes, places elements and reports clicks and moves. Everything else is the map's. Register one with its name, then name it in the map's provider settings.

On Blazor, use CustomMapProvider with the name you registered in your page's JavaScript.

React setup

MapLibre, which draws every provider but Google, loads a worker script from a file of its own. Install maplibre-gl beside @powerportalspro/react-maps, and tell it where your bundler put the worker once, before a map is drawn. Blazor needs no setup.

React Blazor

Map Class

Parameters

Name
Type
Default
Description
Addressstring?
AddressSearchCountriesIReadOnlyList<string>?
AllowFilteringbool
False
AllowNearSearchbool
False
AllowSavedViewsbool
False
CenterMapPosition?
Clusterbool
True
ColorColumnstring?
DefaultTablesIEnumerable<string>?
DefaultViewIdGuid?
FetchXmlstring?
FiltersIReadOnlyList<QueryFilter>?
FitItemIdsIReadOnlyList<string>?
Heightstring
400px
ItemsIReadOnlyList<MapItem>?
LatitudeColumnstring
address1_latitude
LongitudeColumnstring
address1_longitude
MapStyleMapStyle
Road
MaxRowsint
1000
MultiTablebool
False
NativeOptionsIReadOnlyDictionary<string, Object>?
NearDistancedouble?
NearDistancesIReadOnlyList<double>?
NearDistanceUnitDistanceUnit
Miles
PopupTemplateRenderFragment<MapItem>?
PrintEntriesIReadOnlyList<MapPrintEntry>?
PrintEntriesProviderFunc<IReadOnlyList<MapPrintEntry>>?
PrintTitlestring?
ProviderMapProvider?
RecordColorstring?
RecordColorSelectorFunc<TableRecord, string>?
RecordUrlstring?
RecordUrlSelectorFunc<TableRecord, string>?
RegardingTableRecordReference?
SavedViewsKeystring?
ShowAddressSearchbool
False
ShowLegendbool
True
ShowPopupsbool
True
ShowPrintButtonbool
False
ShowRecordLinksbool
True
SignInUrlstring
Account/Login
TablesIEnumerable<string>?
Titlestring?
TitleColumnstring?
UsePortalThemebool
True
ViewIdGuid?
ViewIdsIEnumerable<Guid>?
Widthstring
100%
Zoomdouble?
Name: Address
Type: string?
Name: AddressSearchCountries
Type: IReadOnlyList<string>?
Name: AllowFiltering
Type: bool
Default: False
Name: AllowNearSearch
Type: bool
Default: False
Name: AllowSavedViews
Type: bool
Default: False
Name: Center
Type: MapPosition?
Name: Cluster
Type: bool
Default: True
Name: ColorColumn
Type: string?
Name: DefaultTables
Type: IEnumerable<string>?
Name: DefaultViewId
Type: Guid?
Name: FetchXml
Type: string?
Name: Filters
Type: IReadOnlyList<QueryFilter>?
Name: FitItemIds
Type: IReadOnlyList<string>?
Name: Height
Type: string
Default: 400px
Name: Items
Type: IReadOnlyList<MapItem>?
Name: LatitudeColumn
Type: string
Default: address1_latitude
Name: LongitudeColumn
Type: string
Default: address1_longitude
Name: MapStyle
Type: MapStyle
Default: Road
Name: MaxRows
Type: int
Default: 1000
Name: MultiTable
Type: bool
Default: False
Name: NativeOptions
Type: IReadOnlyDictionary<string, Object>?
Name: NearDistance
Type: double?
Name: NearDistances
Type: IReadOnlyList<double>?
Name: NearDistanceUnit
Type: DistanceUnit
Default: Miles
Name: PopupTemplate
Type: RenderFragment<MapItem>?
Name: PrintEntries
Type: IReadOnlyList<MapPrintEntry>?
Name: PrintEntriesProvider
Type: Func<IReadOnlyList<MapPrintEntry>>?
Name: PrintTitle
Type: string?
Name: Provider
Type: MapProvider?
Name: RecordColor
Type: string?
Name: RecordColorSelector
Type: Func<TableRecord, string>?
Name: RecordUrl
Type: string?
Name: RecordUrlSelector
Type: Func<TableRecord, string>?
Name: Regarding
Type: TableRecordReference?
Name: SavedViewsKey
Type: string?
Name: ShowAddressSearch
Type: bool
Default: False
Name: ShowLegend
Type: bool
Default: True
Name: ShowPopups
Type: bool
Default: True
Name: ShowPrintButton
Type: bool
Default: False
Name: ShowRecordLinks
Type: bool
Default: True
Name: SignInUrl
Type: string
Default: Account/Login
Name: Tables
Type: IEnumerable<string>?
Name: Title
Type: string?
Name: TitleColumn
Type: string?
Name: UsePortalTheme
Type: bool
Default: True
Name: ViewId
Type: Guid?
Name: ViewIds
Type: IEnumerable<Guid>?
Name: Width
Type: string
Default: 100%
Name: Zoom
Type: double?

Events

Name
Type
Description
OnAddressSelectedEventCallback<MapPlace>
OnErrorEventCallback<string>
OnItemClickEventCallback<MapItemClickEventArgs>
OnMapClickEventCallback<MapClickEventArgs>
OnPrintChangedEventCallback<MapPrintState>
OnViewChangedEventCallback<MapView>
Name: OnAddressSelected
Type: EventCallback<MapPlace>
Name: OnError
Type: EventCallback<string>
Name: OnItemClick
Type: EventCallback<MapItemClickEventArgs>
Name: OnMapClick
Type: EventCallback<MapClickEventArgs>
Name: OnPrintChanged
Type: EventCallback<MapPrintState>
Name: OnViewChanged
Type: EventCallback<MapView>

Methods

Name
Parameters
Type
Description
ClearAddressAsyncTask
ClosePopupAsyncTask
FitToItemsAsyncIEnumerable<string> ids
Task
GetMapSettingsAsyncstring provider
Task<MapSettingsResponse>
GetNativeMapAsyncTask<IJSObjectReference>
GetViewAsyncTask<MapView>
GoToAddressAsyncstring text
Task<MapPlace>
NotifyAddressSelectedAsyncMapPlace place
Task
NotifyAddressStatusAsyncstring status
Task
NotifyClusterClickAsyncstring[] ids
MapPosition position
Task
NotifyErrorAsyncstring kind
string message
Task
NotifyItemClickAsyncstring id
MapPosition position
Task
NotifyMapClickAsyncMapPosition position
Task
NotifyViewChangedAsyncMapView view
Task
OpenPopupAsyncstring id
Task
PrintAsyncTask
SaveImageAsyncstring fileName
Task<bool>
SetCameraAsyncMapCamera camera
bool animate
Task
Name: ClearAddressAsync
Type: Task
Name: ClosePopupAsync
Type: Task
Name: FitToItemsAsync
Parameters: IEnumerable<string> ids
Type: Task
Name: GetMapSettingsAsync
Parameters: string provider
Type: Task<MapSettingsResponse>
Name: GetNativeMapAsync
Type: Task<IJSObjectReference>
Name: GetViewAsync
Type: Task<MapView>
Name: GoToAddressAsync
Parameters: string text
Type: Task<MapPlace>
Name: NotifyAddressSelectedAsync
Parameters: MapPlace place
Type: Task
Name: NotifyAddressStatusAsync
Parameters: string status
Type: Task
Name: NotifyClusterClickAsync
Parameters: string[] ids
MapPosition position
Type: Task
Name: NotifyErrorAsync
Parameters: string kind
string message
Type: Task
Name: NotifyItemClickAsync
Parameters: string id
MapPosition position
Type: Task
Name: NotifyMapClickAsync
Parameters: MapPosition position
Type: Task
Name: NotifyViewChangedAsync
Parameters: MapView view
Type: Task
Name: OpenPopupAsync
Parameters: string id
Type: Task
Name: PrintAsync
Type: Task
Name: SaveImageAsync
Parameters: string fileName
Type: Task<bool>
Name: SetCameraAsync
Parameters: MapCamera camera
bool animate
Type: Task