Último clic: Nada todavía
Map Grafica las filas de Dataverse desde una vista o consulta en su latitud y longitud, junto a marcadores, líneas, áreas y círculos propios. Un componente, igual que en Blazor y React, dibujado por el proveedor que elijas: Azure Maps, Esri, Google, OpenStreetMap o cualquier estilo MapLibre.
Doce oficinas como marcadores, una ruta, una zona de entrega y una región de ventas. Los marcadores muy cercanos se agrupan en grupos que se acercan al hacer clic. Haz clic en una oficina para ver su ventana emergente, aquí el contenido propio de la página; la ruta y las áreas no se abren ninguna, ya que la página responde a sus clics por sí misma, y la línea bajo el mapa muestra lo que ha oído.
Último clic: Nada todavía
Las cuentas activas, coloreadas por su tipo de relación o con el interruptor de contactos, se leen mediante una consulta FetchXML con los permisos del visitante y se colocan en su latitud y longitud. Haz clic en una para ver su ventana emergente: las otras columnas de la consulta bajo el nombre de la fila, que abre su página. El recuadro sobre el mapa encuentra una dirección con el proveedor del portal y la fija.
Último clic: Nada todavía
Dirección encontrada: Busca uno en la caja sobre el mapa
La configuración del portal nombra a su proveedor y contiene las claves de cada proveedor en la PowerPortalsPro:Maps sección, por lo que un mapa necesita no nombrar ninguno y ninguna página contiene una clave. Marcadores, grupos, ventanas emergentes y clics funcionan igual en todos los proveedores:
Esri:ApiKey, y cualquier estilo de mapa base de ArcGIS en lugar de calles.Google:ApiKey, y un ID de mapa para un mapa estilizado en Google Cloud.OpenStreetMap:TileUrl producción.MapLibre:StyleUrl o MapLibre:TileUrl, como MapTiler, OpenFreeMap, Stadia o tu propio 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" }
}
}
}
Las claves de Esri y Google llegan al navegador del espectador, que las envía al proveedor, como hacen todos los mapas web. Restringe cada una a las direcciones de tu portal en la consola del proveedor.
Una página nombra a un proveedor solo para usar otro que no sea el del portal, y solo dice cómo se ve: un estilo, un ID de mapa, una vista de los bordes en disputa. Lo que deja sin configurar proviene de la configuración. En un tema oscuro, el mapa cambia al estilo oscuro del proveedor; UsePortalTheme lo apaga.
<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" />Un mapa de Azure Maps inicia sesión con tokens Entra que el portal emite a su propia identidad, la que se conecta a Dataverse, por lo que no se necesita ninguna clave de Azure Maps ni llega al navegador. Asigna a esa identidad el rol de Lector de Datos de Azure Maps en la cuenta de Azure Maps y nada más, y pon AzureMaps:ClientId el ID del cliente en la página de Autenticación de la cuenta.
Cada espectador mantiene el token mientras se ejecuta, así que solo puede hacer lo que ese rol permite; limita también los orígenes permitidos de la cuenta a las direcciones de tu portal. El mapa pide un token nuevo antes de que expire. Una página lo pide una vez, y un espectador puede recibir 120 tokens por minuto salvo PowerPortalsPro:Maps:RateLimit que diga lo contrario; los visitantes que no han iniciado sesión cuentan por su dirección, que comparten todos los que estén detrás de la conexión de una oficina.
Da una vista o una consulta FetchXML y cada fila con latitud y longitud se convierte en un marcador nombrado por el nombre principal de la tabla. Las filas se leen con permisos del usuario, como las de una cuadrícula, y al hacer clic en una se muestran las columnas de su vista.
<Map
viewId={activeAccountsViewId}
latitudeColumn="address1_latitude"
longitudeColumn="address1_longitude"
recordColor="var(--colorPaletteRedBackground3)"
/><Map ViewId="activeAccountsViewId"
LatitudeColumn="address1_latitude"
LongitudeColumn="address1_longitude"
RecordColor="var(--colorPaletteRedBackground3)" />Las filas sin ambas coordenadas quedan fuera de la consulta. El mapa lee 1.000 filas salvo MaxRows que diga lo contrario (como máximo 5.000) y anota en el mapa cuando hay más.
Las filas sin coordenadas pueden completarlas a partir de sus direcciones a medida que se guardan: véase Geocodificación.
Con MultiTable (React: multiTable), el mapa muestra las filas de cada tabla que el servidor registra para mapas con ForMaps, cada tabla en su propio color con una leyenda. El registro nombra las columnas que contienen la latitud y longitud de una fila, que siempre se leen. DefaultTables elige las tablas que se muestran primero y Tables las que puede mostrar; Regarding lo reduce a las filas alrededor de un 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"));
Una tabla cuyas coordenadas están registradas con AddMapCoordinates, o rellenadas con AddGeocoding, se registra para mapas con ForMaps() solo: véase Geocodificación.
<Map multiTable defaultTables={['account']} allowSavedViews /><Map MultiTable="true" DefaultTables="@(["account"])" AllowSavedViews="true" />Una barra de herramientas sobre el mapa elige la vista: cada tabla, o una vista de varias tablas registradas con AddMultiTableView y ForMaps(). ViewIds (React: viewIds) ofrece solo las vistas registradas que nombra, en su orden, y DefaultViewId (defaultViewId) la que se abre. Su menú de Filtros activa o desactiva las tablas y edita los filtros de cada tabla. Con AllowSavedViews (React: allowSavedViews), un usuario iniciado sesión puede guardar lo que el mapa muestra como una vista propia: véase Vistas guardadas.
Con AllowNearSearch (React: allowNearSearch), un mapa de varias tablas tiene un botón Cerca en su barra de herramientas. Encuentra un lugar — una dirección, con la búsqueda del proveedor del mapa, o la ubicación del visitante, que el navegador le pide que comparta — y el mapa muestra solo las filas dentro de la distancia elegida, las más cercanas en todas las tablas: el círculo está dibujado, el mapa se ajusta a él, y la ventana emergente de cada fila indica la distancia que tiene. NearDistances (nearDistances) establece las distancias ofrecidas, 1, 5, 10, 25, 50 y 100 a menos que esté configurado, NearDistance (nearDistance) la que inicia, 25 si no está configurado, y NearDistanceUnit (nearDistanceUnit) si empieza en millas, el valor por defecto o kilómetros; el visitante puede cambiar. Véalo en la demo de Cercano.
<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 tabla se lee por sus coordenadas registradas, con permisos del visitante: el servidor lee el cuadrado alrededor del círculo, mantiene las filas dentro de la distancia por su distancia sobre el suelo y devuelve la más cercana. Una búsqueda cerca de un lugar mantiene como máximo 500 filas, las más cercanas, y la leyenda del mapa indica cuándo había más; NearSearchOptions.MaximumMatches sube o baja esa cifra, hasta 5.000. Un visitante que puede no leer las coordenadas de una tabla no encuentra ninguna de sus filas.
// Program.cs
builder.Services.Configure<NearSearchOptions>(options => options.MaximumMatches = 1_000);
Al hacer clic en un elemento, un marcador o una línea, área o círculo igual, se OnItemClick eleva con el objeto, sus datos y el lugar donde se ha hecho clic, y luego se abre una ventana emergente sobre él. La ventana emergente es una tarjeta Fluent con tu propio contenido: una plantilla en Blazor, una función de renderizado en React. Un clic en el propio mapa se OnMapClick eleva con su posición.
<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 un clic a tu manera, abre la página de un registro, por ejemplo, o selecciona una región, llama PreventDefault() al clic (preventDefault() en React) y el mapa no abre ninguna ventana emergente para ello. ShowPopups Configura para que False desactiva ventanas emergentes para cada elemento.
<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}");
}
}
}El encabezado emergente de una fila enlaza a la página de su registro, la registrada para su tabla con AddRecordPage (ver Páginas de registro), en un mapa de una tabla o varias. RecordUrl La enlaza en otro lugar: una URL con {0} en lugar del id de la fila y {1} el de su tabla. ShowRecordLinks (React: showRecordLinks) configurado como falso desactiva los enlaces. Un elemento de tus propios enlaces enlaza con su Url. En React, onNavigate sigue el enlace con tu router.
Los marcadores en un lugar, como los contactos en una dirección, nunca se separan al hacer zoom, así que su grupo los lista en su lugar.
ColorColumn colorea el marcador de cada fila según su opción en una columna de elección, estado, motivo de estado o sí/no: el color que tiene la opción en Dataverse, o una de las paletas de Fluent cuando no tiene ninguno, con una leyenda de las opciones en el mapa. Las filas sin valor son grises. Para cualquier otra regla, RecordColorSelector en Blazor y una función para recordColor en React dan a cada fila su propio color.
<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 un cuadro de búsqueda sobre la esquina del mapa. Encuentra direcciones y lugares con el propio proveedor del portal, coloca los lugares en vista primero y fija el elegido; OnAddressSelected entrega el lugar a la página y lo anula una vez que la búsqueda está completada. AddressSearchCountries Lo mantiene en algunos países y GoToAddressAsync (goToAddress en la referencia de React) busca desde el 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 y Google sugieren lugares como tipos de visitantes. La clave de Google necesita activar la API de Lugares (Nuevo), y la clave de Esri su privilegio de geocodificación. Los mapas de OpenStreetMap y MapLibre buscan en Nominatim de OpenStreetMap, que su política de uso permite solo para uso ligero y nunca cuando alguien escribe, así que buscan cuando se pulsa Enter; nombra tu propio servidor Nominatim en OpenStreetMap:GeocoderUrl o MapLibre:GeocoderUrl para más.
Address (address en React) muestra una dirección sin cuadro de búsqueda: el mapa la encuentra con la misma búsqueda una vez que se dibuja, y de nuevo cada vez que cambia, la fija y hace zoom. Un spinner muestra mientras mira, y un mensaje cuando no se encuentra. Es lo que AddressDisplayValueabre el pin del mapa para una dirección sin coordenadas.
<Map address="1 Microsoft Way, Redmond, WA 98052" height="300px" /><Map Address="1 Microsoft Way, Redmond, WA 98052" Height="300px" />ShowPrintButton (showPrintButton en React) añade un botón Imprimir — en la barra de herramientas de un MultiTable mapa, o bien sobre la esquina superior derecha del mapa — que imprime el mapa tal como lo muestra, con sus pines, su leyenda, la ventana emergente abierta y la atribución del proveedor. PrintTitle es el encabezado que está encima, y PrintEntries lista los registros debajo, cada uno junto a la etiqueta de su PIN. El Label de un marcador se dibuja en su pin en lugar del punto: da un número a las paradas y la impresión se lee como una hoja de ruta. PrintAsync() (print() en la referencia React) imprime desde un botón propio de la página, y OnPrintChanged (onPrintChange) indica cuándo se puede imprimir el mapa y si puede guardarse como imagen, para que ese botón siga el siguiente.
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 y MapLibre se imprimen como una imagen del mapa, por lo que la página alrededor del mapa queda fuera y el mapa no se mueve; el mismo botón también guarda esa imagen como archivo PNG (SaveImageAsync, o saveImage()). Se dibuja una ventana emergente abierta en la imagen con su título y campos, o los marcadores que lista; una que muestra el contenido propio de la página (PopupTemplate, o renderPopup) se dibuja con el título de su elemento. Un mapa de Google no puede copiarse en una imagen, así que se imprime tal y como está en la página, ventana emergente incluida, el resto de la página queda fuera de la impresión y no ofrece imagen para guardar. Un proveedor propio se imprime como imagen cuando su superficie implementa capture.
NativeOptions se pasan al propio mapa del proveedor a medida que se crea, sobre el propio mapa. Eso activa Street View para Google, por ejemplo, o permite que un mapa de MapLibre haga zoom solo con la rueda de desplazamiento (cooperativeGestures: false); por defecto necesita Ctrl, como hace Google en una página que desplaza, así que la página pasa más allá de ella. El objeto mapa del proveedor también está disponible para tu propio JavaScript: GetNativeMapAsync en Blazor, native en la referencia de React.
<Map provider={{ name: 'google' }} nativeOptions={{ streetViewControl: true }} /><Map Provider="@(new GoogleMapProvider())"
NativeOptions="@(new Dictionary<string, object?> { ["streetViewControl"] = true })" />Un proveedor dibuja el mapa base y las formas, coloca elementos y reporta clics y movimientos. Todo lo demás es del mapa. Regístralo con su nombre y ponle nombre en la configuración del proveedor del 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.
},
});
En Blazor, usa CustomMapProvider con el nombre que registraste en JavaScript de tu página.
MapLibre, que dibuja todos los proveedores excepto Google, carga un script de trabajo desde un archivo propio. Instálalo maplibre-gl junto @powerportalspro/react-mapsa , y dile dónde ha puesto tu bundler al trabajador una vez, antes de dibujar un mapa. Blazor no necesita configuración.
// main.tsx, with Vite
import { setMapLibreWorkerUrl } from '@powerportalspro/react-maps';
import workerUrl from 'maplibre-gl/dist/maplibre-gl-worker.mjs?worker&url';
setMapLibreWorkerUrl(workerUrl);
Nombre | Tipo | Default | Descripción |
|---|---|---|---|
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? |
AddressAddressSearchCountriesAllowFilteringAllowNearSearchAllowSavedViewsCenterClusterColorColumnDefaultTablesDefaultViewIdFetchXmlFiltersFitItemIdsHeightItemsLatitudeColumnLongitudeColumnMapStyleMaxRowsMultiTableNativeOptionsNearDistanceNearDistancesNearDistanceUnitPopupTemplatePrintEntriesPrintEntriesProviderPrintTitleProviderRecordColorRecordColorSelectorRecordUrlRecordUrlSelectorRegardingSavedViewsKeyShowAddressSearchShowLegendShowPopupsShowPrintButtonShowRecordLinksSignInUrlTablesTitleTitleColumnUsePortalThemeViewIdViewIdsWidthZoomNombre | Tipo | Descripción |
|---|---|---|
OnAddressSelected | EventCallback<MapPlace> | |
OnError | EventCallback<string> | |
OnItemClick | EventCallback<MapItemClickEventArgs> | |
OnMapClick | EventCallback<MapClickEventArgs> | |
OnPrintChanged | EventCallback<MapPrintState> | |
OnViewChanged | EventCallback<MapView> |
OnAddressSelectedOnErrorOnItemClickOnMapClickOnPrintChangedOnViewChangedNombre | Parámetros | Tipo | Descripción |
|---|---|---|---|
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