Último clique: Nada ainda
Map plota linhas do Dataverse a partir de uma visualização ou consulta em sua latitude e longitude, ao lado de marcadores, linhas, áreas e círculos próprios. Um componente, o mesmo em Blazor e React, desenhado pelo provedor que você escolher: Azure Maps, Esri, Google, OpenStreetMap ou qualquer estilo MapLibre.
Doze escritórios como marcadores, uma rota, uma zona de entrega e uma região de vendas. Marcadores próximos uns dos outros se agrupam em agrupamentos, que dão zoom quando clicados. Clique em um escritório para seu popup, aqui o conteúdo da própria página; a rota e as áreas não abrem nenhuma, pois a página responde aos seus cliques, e a linha abaixo do mapa mostra o que ouviu.
Último clique: Nada ainda
As contas ativas, coloridas pelo tipo de relacionamento, ou com a troca de contatos, são lidas por uma consulta FetchXML com as permissões do visitante e colocadas em sua latitude e longitude. Clique em uma para ver seu pop-up: as outras colunas da consulta sob o nome da linha, que abre sua página. A caixa sobre o mapa encontra um endereço com o provedor do portal e o fixa.
Último clique: Nada ainda
Endereço encontrado: Procure um na caixa sobre o mapa
A configuração do portal nomeia seu provedor e mantém as chaves de cada provedor, na PowerPortalsPro:Maps seção, então um mapa precisa não nomear nenhum e nenhuma página contém uma chave. Marcadores, clusters, popups e cliques funcionam da mesma forma em todos os provedores:
Esri:ApiKey, e qualquer estilo de mapa-base do ArcGIS no lugar das ruas.Google:ApiKey, e um ID de mapa para um mapa estilizado no Google Cloud.OpenStreetMap:TileUrl produção.MapLibre:StyleUrl ou MapLibre:TileUrl, como MapTiler, OpenFreeMap, Stadia ou seu próprio servidor.// appsettings.json; keep the keys with the portal's other secrets
{
"PowerPortalsPro": {
"Maps": {
"Provider": "AzureMaps",
"AzureMaps": { "ClientId": "the Azure Maps account's client id" },
"Esri": { "ApiKey": "an ArcGIS API key" },
"Google": { "ApiKey": "a Maps JavaScript API key", "MapId": "a map id" }
}
}
}
As chaves da Esri e do Google chegam ao navegador do visualizador, que as envia para o provedor, como todos os mapas da web. Restringa cada uma aos endereços do seu portal no console do provedor.
Uma página nomeia um provedor apenas para usar outro que não o portal, e diz apenas como ele se apresenta: um estilo, um ID de mapa, uma visão das bordas disputadas. O que ela deixa sem definir vem da configuração. Em um tema escuro, o mapa muda para o estilo escuro do provedor; UsePortalTheme desliga isso.
<Map items={items} />
<Map provider={{ name: 'google', mapId: 'a map id' }} items={items} />
<Map provider={{ name: 'esri', styleName: 'arcgis/navigation' }} items={items} />
<Map provider={{ name: 'mapLibre', styleUrl: 'https://tiles.openfreemap.org/styles/liberty' }} items={items} /><Map Items="items" />
<Map Provider="@(new GoogleMapProvider { MapId = "a map id" })" Items="items" />
<Map Provider="@(new EsriMapProvider { StyleName = "arcgis/navigation" })" Items="items" />
<Map Provider="@(new MapLibreMapProvider { StyleUrl = "https://tiles.openfreemap.org/styles/liberty" })" Items="items" />Um mapa do Azure Maps faz login com tokens Entra que o portal emite para sua própria identidade, aquela que ele se conecta ao Dataverse, então nenhuma chave do Azure Maps é necessária ou chega ao navegador. Dê a essa identidade o papel de Leitor de Dados do Azure Maps na conta Azure Maps e nada mais, e defina AzureMaps:ClientId para o id do cliente na página de Autenticação da conta.
Todo visualizador mantém o token enquanto ele está em execução, então ele só pode fazer o que esse papel permite; limite as origens permitidas da conta aos endereços do seu portal também. O mapa pede um novo token antes de expirar. Uma página pede uma vez, e um espectador pode receber 120 tokens por minuto, a menos que PowerPortalsPro:Maps:RateLimit diga o contrário; visitantes que não estão logados são contados pelo endereço, que todos os responsáveis pela conexão de um escritório compartilham.
Dê uma visualização ou uma consulta FetchXML, e cada linha com latitude e longitude se torna um marcador nomeado pelo nome principal da tabela. As linhas são lidas com as permissões do usuário, assim como as de uma grade, e ao clicar em uma mostra as colunas da visualização.
<Map
viewId={activeAccountsViewId}
latitudeColumn="address1_latitude"
longitudeColumn="address1_longitude"
recordColor="var(--colorPaletteRedBackground3)"
/><Map ViewId="activeAccountsViewId"
LatitudeColumn="address1_latitude"
LongitudeColumn="address1_longitude"
RecordColor="var(--colorPaletteRedBackground3)" />Linhas sem ambas as coordenadas são deixadas de fora da consulta. O mapa indica 1.000 linhas, a menos que MaxRows diga o contrário (no máximo 5.000) e anota no mapa quando há mais.
Linhas sem coordenadas podem tê-las preenchidas a partir de seus endereços à medida que são salvas: veja Geocodificação.
Com MultiTable (Reagir: multiTable), o mapa mostra as linhas de cada tabela que o servidor registra para mapas com ForMaps, cada tabela em sua própria cor com uma legenda. O registro nomeia as colunas que contêm a latitude e longitude de uma linha, que são sempre lidas. DefaultTables escolhe as tabelas mostradas inicialmente e Tables as que pode mostrar; Regarding reduz para as linhas ao redor de um registro.
// Program.cs
builder.Services.AddMultiTableSource("account", t => t
.Columns("address1_city")
.ForMaps(latitude: "address1_latitude", longitude: "address1_longitude"));
builder.Services.AddMultiTableSource("contact", t => t
.ForMaps(latitude: "address1_latitude", longitude: "address1_longitude"));
Uma tabela cujas coordenadas são registradas com AddMapCoordinates, ou preenchidas por AddGeocoding, é registrada para mapas com apenas com ForMaps() : veja Geocodificação.
<Map multiTable defaultTables={['account']} allowSavedViews /><Map MultiTable="true" DefaultTables="@(["account"])" AllowSavedViews="true" />Uma barra de ferramentas sobre o mapa escolhe a visualização: cada tabela, ou uma visualização de várias tabelas registradas com AddMultiTableView e ForMaps(). ViewIds (Reagir: viewIds) oferece apenas as visualizações registradas que ela nomeia, em sua ordem, e DefaultViewId (defaultViewId) aquela em que ela se abre. Seu menu de Filtro alterna com as tabelas e edita os filtros de cada tabela. Com AllowSavedViews (Reagir: allowSavedViews), um usuário logado pode salvar o que o mapa mostra como uma visualização própria: veja Visualizações Salvas.
Com AllowNearSearch (Reagir: allowNearSearch), um mapa de várias tabelas possui um botão Perto em sua barra de ferramentas. Ele encontra um lugar — um endereço, com a busca do fornecedor do mapa, ou a localização do visitante, que o navegador pede para compartilhar — e o mapa então mostra apenas as linhas dentro da distância escolhida, as primeiras mais próximas em cada tabela: o círculo é desenhado, o mapa ajustado a ele, e o pop-up de cada linha mostra a distância. NearDistances (nearDistances) define as distâncias oferecidas, 1, 5, 10, 25, 50 e 100, a menos que esteja definida, NearDistance (nearDistance) a que começa, 25 a menos que definida, e NearDistanceUnit (nearDistanceUnit) se começa em milhas, o padrão ou quilômetros; o visitante pode trocar. Veja na demonstração Proximidade.
<Map
multiTable
allowNearSearch
nearDistances={[10, 25, 50, 100]}
nearDistance={25}
nearDistanceUnit={DistanceUnits.Kilometers}
/><Map MultiTable="true"
AllowNearSearch="true"
NearDistances="@([10, 25, 50, 100])"
NearDistance="25"
NearDistanceUnit="DistanceUnit.Kilometers" />Cada tabela é lida pelas coordenadas registradas, com as permissões do visitante: o servidor lê o quadrado ao redor do círculo, mantém as linhas dentro da distância em relação ao chão e retorna a mais próxima. Uma busca perto de um local mantém no máximo 500 linhas, a mais próxima, e a legenda do mapa diz quando havia mais; NearSearchOptions.MaximumMatches aumenta ou diminui isso, até 5.000. Um visitante que pode não ler as coordenadas de uma tabela não encontra nenhuma de suas linhas.
// Program.cs
builder.Services.Configure<NearSearchOptions>(options => options.MaximumMatches = 1_000);
Clicar em um item, marcador ou linha, área ou círculo, aparece OnItemClick com o item, seus dados e onde foi clicado, e então abre um pop-up sobre ele. O popup é um cartão Fluent com seu próprio conteúdo: um template no Blazor, uma função de renderização no React. Um clique no próprio mapa aumenta OnMapClick com sua posição.
<Map
items={items}
onItemClick={({ item, position }) => select(item)}
onMapClick={({ position }) => addStop(position)}
renderPopup={(item) => (
<>
<strong>{item.title}</strong>
<Button onClick={() => open(item)}>Open</Button>
</>
)}
/><Map Items="items" OnItemClick="OnItemClicked" OnMapClick="OnMapClicked">
<PopupTemplate>
<strong>@context.Title</strong>
<FluentButton OnClick="@(() => Open(context))">Open</FluentButton>
</PopupTemplate>
</Map>Para responder a um clique do seu próprio jeito, abra a página de um registro, por exemplo, ou selecione uma região, chame PreventDefault() ao clicar (preventDefault() no React) e o mapa não abre nenhum pop-up para isso. ShowPopups Configure para falso desliga os pop-ups para cada item.
<Map
viewId={accountsViewId}
onItemClick={(event) => {
// A row's marker opens its record's page; anything else its popup.
const row = event.item.data as MapRecord | undefined;
if (row?.record) {
event.preventDefault();
navigate(`/accounts/${row.record.id}`);
}
}}
/><Map ViewId="accountsViewId" OnItemClick="OnItemClicked" />
@code {
// A row's marker opens its record's page; anything else its popup.
private void OnItemClicked(MapItemClickEventArgs args)
{
if (args.Item.Record is { } record)
{
args.PreventDefault();
Navigation.NavigateTo($"accounts/{record.Id}");
}
}
}O cabeçalho popup de uma linha faz um link para a página do seu registro, aquela registrada para sua tabela com AddRecordPage (veja Páginas de Registro), em um mapa de uma tabela ou várias. RecordUrl a vincula para outro lugar: uma URL com {0} no lugar do id da linha e {1} do de sua tabela. ShowRecordLinks (React: showRecordLinks) definido como falso desativa os links. Um item dos seus próprios links para Urlseu . No React, onNavigate segue o link com seu roteador.
Marcadores em um lugar, como os contatos em um endereço, nunca se separam ao dar zoom, então o grupo deles os lista em vez disso.
ColorColumn colorir o marcador de cada linha pela sua opção em uma coluna de escolha, status, motivo de status ou sim/não: a cor que a opção tem no Dataverse, ou uma das paletas do Fluent quando não houver, com uma legenda das opções no mapa. Linhas sem valor são cinzas. Para qualquer outra regra, RecordColorSelector no Blazor e uma função para recordColor no React dão a cada linha sua própria cor.
<Map viewId={accountsViewId} colorColumn="customertypecode" recordUrl="/accounts/{0}" />
<Map viewId={accountsViewId} recordColor={(record) => (isKeyAccount(record) ? 'var(--colorPaletteRedBackground3)' : undefined)} /><Map ViewId="accountsViewId" ColorColumn="customertypecode" RecordUrl="accounts/{0}" />
<Map ViewId="accountsViewId" RecordColorSelector="@(record => IsKeyAccount(record) ? "var(--colorPaletteRedBackground3)" : null)" />ShowAddressSearch coloca uma caixa de busca sobre o canto do mapa. Ele encontra endereços e lugares com o próprio provedor do portal, coloca os lugares em visualização primeiro e fixa o escolhido; OnAddressSelected entrega a página o local, e nulo quando a busca é limpa. AddressSearchCountries mantém em alguns países e GoToAddressAsync (goToAddress na referência do React) faz buscas a partir do código.
<Map
viewId={accountsViewId}
showAddressSearch
addressSearchCountries={['US', 'CA']}
onAddressSelected={(place) => setNear(place?.position ?? null)}
/><Map ViewId="accountsViewId"
ShowAddressSearch="true"
AddressSearchCountries="@(["US", "CA"])"
OnAddressSelected="@(place => _near = place?.Position)" />Azure Maps, Esri e Google sugerem locais como tipos de visitantes. A chave do Google precisa habilitar a API Lugares (Nova), e a chave da Esri seu privilégio de geocodificação. Os mapas OpenStreetMap e MapLibre pesquisam no Nominatim do OpenStreetMap, cuja política de uso permite apenas para uso leve e nunca quando alguém digita, então eles pesquisam quando pressionam Enter; nomeie seu próprio servidor Nominatim em OpenStreetMap:GeocoderUrl ou MapLibre:GeocoderUrl para mais informações.
Address(address no React ) mostra um endereço sem uma caixa de busca: o mapa o encontra com a mesma busca depois de desenhado, e novamente sempre que muda, fixa e dá zoom até ele. Um spinner mostra enquanto ele procura, e uma mensagem quando não é encontrado. É o pinoAddressDisplayValue do mapa que abre para um endereço sem coordenadas.
<Map address="1 Microsoft Way, Redmond, WA 98052" height="300px" /><Map Address="1 Microsoft Way, Redmond, WA 98052" Height="300px" />ShowPrintButton (showPrintButton no React ) adiciona um botão Imprimir — na barra de ferramentas de um MultiTable mapa, ou no canto superior direito do mapa — que imprime o mapa conforme mostra, com seus pins, sua legenda, o popup aberto nele e a atribuição do provedor. PrintTitle é o título acima dele, e PrintEntries lista registros abaixo, cada um ao lado do rótulo do seu pin. O marcador Label é desenhado em seu pin no lugar do ponto: dá um número para as paradas e a impressão é uma folha de rota. PrintAsync() (print() na referência React) imprime a partir de um botão próprio da página, e OnPrintChanged (onPrintChange) diz quando o mapa pode ser impresso e se pode ser salvo como imagem, para que esse botão siga o local.
const stops: MapMarker[] = [{ kind: 'marker', id: '1', label: '1', title: 'Contoso', position: { latitude: 47.61, longitude: -122.33 } }];
<Map
ref={mapRef}
items={stops}
showPrintButton
printTitle="Tuesday's visits"
printEntries={stops.map((stop) => ({ label: stop.label, title: stop.title ?? '' }))}
/>
// The same from a button of the page's own.
mapRef.current?.print();<Map @ref="_map"
Items="stops"
ShowPrintButton="true"
PrintTitle="Tuesday's visits"
PrintEntries="@stops.Select(stop => new MapPrintEntry(stop.Title!, stop.Label)).ToList()" />
@code {
// A marker's Label is drawn on its pin, and beside it in the printed list.
private List<MapMarker> stops = [new() { Id = "1", Label = "1", Title = "Contoso", Position = new(47.61, -122.33) }];
private Map? _map;
// The same from a button of the page's own.
private Task PrintAsync() => _map!.PrintAsync();
}Azure Maps, Esri, OpenStreetMap e mapas MapLibre são impressos como uma imagem do mapa, então a página ao redor do mapa fica de fora e o mapa não se move; o mesmo botão também salva essa imagem como um arquivo PNG (SaveImageAsync, ou saveImage()). Um popup aberto é desenhado na imagem com seu título e campos, ou nos marcadores que ela lista; um que mostra o conteúdo da própria página (PopupTemplate, ou renderPopup) é desenhado com o título do item. Um mapa do Google não pode ser copiado para uma imagem, então ele é impresso como está na página, popup e tudo, o restante da página fica fora da impressão e não oferece imagem para salvar. Um provedor seu próprio é impresso como imagem quando sua superfície implementa capture.
NativeOptions são passadas para o próprio mapa do provedor conforme é feito, sobre o próprio mapa. Isso ativa o Street View para o Google, por exemplo, ou permite que um mapa do MapLibre faça zoom apenas com a roda de rolagem (cooperativeGestures: false); por padrão, ele precisa de Ctrl, assim como o do Google em uma página que rola, para que a página role além dela. O objeto mapa do provedor também está disponível para seu próprio JavaScript: GetNativeMapAsync no Blazor, native na referência React.
<Map provider={{ name: 'google' }} nativeOptions={{ streetViewControl: true }} /><Map Provider="@(new GoogleMapProvider())"
NativeOptions="@(new Dictionary<string, object?> { ["streetViewControl"] = true })" />Um provedor desenha o mapa base e as formas, coloca elementos e reporta cliques e movimentos. Todo o resto é do mapa. Registre um com seu nome e depois dê um nome nas configurações do provedor do mapa.
import { registerMapProvider, type MapSurface } from '@powerportalspro/react-maps';
registerMapProvider({
name: 'arcgis-sdk',
async create(context): Promise<MapSurface> {
// Draw into context.container with your library; report clicks and moves through
// context.events; return the surface: camera, projection, overlays and shapes.
},
});
No Blazor, use CustomMapProvider com o nome que você registrou no JavaScript da sua página.
O MapLibre, que desenha todos os provedores menos o Google, carrega um script worker a partir de um arquivo próprio. Instale maplibre-gl ao lado @powerportalspro/react-mapsde , e diga onde seu bundler colocou o worker uma vez, antes de desenhar um mapa. Blazor não precisa de configuração.
// main.tsx, with Vite
import { setMapLibreWorkerUrl } from '@powerportalspro/react-maps';
import workerUrl from 'maplibre-gl/dist/maplibre-gl-worker.mjs?worker&url';
setMapLibreWorkerUrl(workerUrl);
Nome | Tipo | Padrão | Descrição |
|---|---|---|---|
Address | string? | ||
AddressSearchCountries | IReadOnlyList<string>? | ||
AllowFiltering | bool | False | |
AllowNearSearch | bool | False | |
AllowSavedViews | bool | False | |
Center | MapPosition? | ||
Cluster | bool | True | |
ColorColumn | string? | ||
DefaultTables | IEnumerable<string>? | ||
DefaultViewId | Guid? | ||
FetchXml | string? | ||
Filters | IReadOnlyList<QueryFilter>? | ||
FitItemIds | IReadOnlyList<string>? | ||
Height | string | 400px | |
Items | IReadOnlyList<MapItem>? | ||
LatitudeColumn | string | address1_latitude | |
LongitudeColumn | string | address1_longitude | |
MapStyle | MapStyle | Road | |
MaxRows | int | 1000 | |
MultiTable | bool | False | |
NativeOptions | IReadOnlyDictionary<string, Object>? | ||
NearDistance | double? | ||
NearDistances | IReadOnlyList<double>? | ||
NearDistanceUnit | DistanceUnit | Miles | |
PopupTemplate | RenderFragment<MapItem>? | ||
PrintEntries | IReadOnlyList<MapPrintEntry>? | ||
PrintEntriesProvider | Func<IReadOnlyList<MapPrintEntry>>? | ||
PrintTitle | string? | ||
Provider | MapProvider? | ||
RecordColor | string? | ||
RecordColorSelector | Func<TableRecord, string>? | ||
RecordUrl | string? | ||
RecordUrlSelector | Func<TableRecord, string>? | ||
Regarding | TableRecordReference? | ||
SavedViewsKey | string? | ||
ShowAddressSearch | bool | False | |
ShowLegend | bool | True | |
ShowPopups | bool | True | |
ShowPrintButton | bool | False | |
ShowRecordLinks | bool | True | |
SignInUrl | string | Account/Login | |
Tables | IEnumerable<string>? | ||
Title | string? | ||
TitleColumn | string? | ||
UsePortalTheme | bool | True | |
ViewId | Guid? | ||
ViewIds | IEnumerable<Guid>? | ||
Width | string | 100% | |
Zoom | double? |
AddressAddressSearchCountriesAllowFilteringAllowNearSearchAllowSavedViewsCenterClusterColorColumnDefaultTablesDefaultViewIdFetchXmlFiltersFitItemIdsHeightItemsLatitudeColumnLongitudeColumnMapStyleMaxRowsMultiTableNativeOptionsNearDistanceNearDistancesNearDistanceUnitPopupTemplatePrintEntriesPrintEntriesProviderPrintTitleProviderRecordColorRecordColorSelectorRecordUrlRecordUrlSelectorRegardingSavedViewsKeyShowAddressSearchShowLegendShowPopupsShowPrintButtonShowRecordLinksSignInUrlTablesTitleTitleColumnUsePortalThemeViewIdViewIdsWidthZoomNome | Tipo | Descrição |
|---|---|---|
OnAddressSelected | EventCallback<MapPlace> | |
OnError | EventCallback<string> | |
OnItemClick | EventCallback<MapItemClickEventArgs> | |
OnMapClick | EventCallback<MapClickEventArgs> | |
OnPrintChanged | EventCallback<MapPrintState> | |
OnViewChanged | EventCallback<MapView> |
OnAddressSelectedOnErrorOnItemClickOnMapClickOnPrintChangedOnViewChangedNome | Parâmetros | Tipo | Descrição |
|---|---|---|---|
ClearAddressAsync | Task | ||
ClosePopupAsync | Task | ||
FitToItemsAsync | IEnumerable<string> ids | Task | |
GetMapSettingsAsync | string provider | Task<MapSettingsResponse> | |
GetNativeMapAsync | Task<IJSObjectReference> | ||
GetViewAsync | Task<MapView> | ||
GoToAddressAsync | string text | Task<MapPlace> | |
NotifyAddressSelectedAsync | MapPlace place | Task | |
NotifyAddressStatusAsync | string status | Task | |
NotifyClusterClickAsync | string[] ids MapPosition position | Task | |
NotifyErrorAsync | string kind string message | Task | |
NotifyItemClickAsync | string id MapPosition position | Task | |
NotifyMapClickAsync | MapPosition position | Task | |
NotifyViewChangedAsync | MapView view | Task | |
OpenPopupAsync | string id | Task | |
PrintAsync | Task | ||
SaveImageAsync | string fileName | Task<bool> | |
SetCameraAsync | MapCamera camera bool animate | Task |
ClearAddressAsyncClosePopupAsyncFitToItemsAsyncGetMapSettingsAsyncGetNativeMapAsyncGetViewAsyncGoToAddressAsyncNotifyAddressSelectedAsyncNotifyAddressStatusAsyncNotifyClusterClickAsyncNotifyErrorAsyncNotifyItemClickAsyncNotifyMapClickAsyncNotifyViewChangedAsyncOpenPopupAsyncPrintAsyncSaveImageAsyncSetCameraAsync