Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5dbed75cbc |
@@ -473,6 +473,10 @@ A map from a collaborative free geographic world database.
|
|||||||
- **Opacity** from 0 (transparent) to 1 (opaque)
|
- **Opacity** from 0 (transparent) to 1 (opaque)
|
||||||
- **Display tooltip** - allows you to toggle tooltips for the layer.
|
- **Display tooltip** - allows you to toggle tooltips for the layer.
|
||||||
|
|
||||||
|
{{< admonition type="note" >}}
|
||||||
|
OpenStreetMap requires attribution by license. When you use this layer, attribution automatically displays in the map controls and cannot be hidden.
|
||||||
|
{{< /admonition >}}
|
||||||
|
|
||||||
[About Open Street Map](https://www.openstreetmap.org/about)
|
[About Open Street Map](https://www.openstreetmap.org/about)
|
||||||
|
|
||||||
#### CARTO basemap layer
|
#### CARTO basemap layer
|
||||||
@@ -489,6 +493,10 @@ A CARTO layer is from CARTO Raster basemaps.
|
|||||||
- **Opacity** from 0 (transparent) to 1 (opaque)
|
- **Opacity** from 0 (transparent) to 1 (opaque)
|
||||||
- **Display tooltip** - allows you to toggle tooltips for the layer.
|
- **Display tooltip** - allows you to toggle tooltips for the layer.
|
||||||
|
|
||||||
|
{{< admonition type="note" >}}
|
||||||
|
CARTO requires attribution by license. When you use this layer, attribution automatically displays in the map controls and cannot be hidden.
|
||||||
|
{{< /admonition >}}
|
||||||
|
|
||||||
[About CARTO](https://carto.com/about-us/)
|
[About CARTO](https://carto.com/about-us/)
|
||||||
|
|
||||||
#### ArcGIS MapServer layer
|
#### ArcGIS MapServer layer
|
||||||
@@ -654,6 +662,10 @@ Enables the mouse wheel to be used for zooming in or out.
|
|||||||
|
|
||||||
Displays attribution for basemap layers.
|
Displays attribution for basemap layers.
|
||||||
|
|
||||||
|
{{< admonition type="note" >}}
|
||||||
|
Attribution is required by license for certain map layers, including OpenStreetMap and CARTO. When you use these layers, their attribution automatically displays and cannot be hidden. The toggle becomes **Show optional attribution** and controls whether attribution from other layers is also displayed.
|
||||||
|
{{< /admonition >}}
|
||||||
|
|
||||||
{{< figure src="/static/img/docs/geomap-panel/geomap-map-controls-attribution-9-1-0.png" max-width="400px" alt="Geomap panel attribution" >}}
|
{{< figure src="/static/img/docs/geomap-panel/geomap-map-controls-attribution-9-1-0.png" max-width="400px" alt="Geomap panel attribution" >}}
|
||||||
|
|
||||||
#### Show scale
|
#### Show scale
|
||||||
|
|||||||
@@ -67,6 +67,12 @@ export interface MapLayerRegistryItem<TConfig = MapLayerOptions> extends Registr
|
|||||||
*/
|
*/
|
||||||
hideOpacity?: boolean;
|
hideOpacity?: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates that this layer requires attribution to be shown by license
|
||||||
|
* When true, the attribution control will always be displayed regardless of user settings
|
||||||
|
*/
|
||||||
|
requiresAttribution?: boolean;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function that configures transformation and returns a transformer
|
* Function that configures transformation and returns a transformer
|
||||||
* @param options
|
* @param options
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import Zoom from 'ol/control/Zoom';
|
|||||||
import { Coordinate } from 'ol/coordinate';
|
import { Coordinate } from 'ol/coordinate';
|
||||||
import { isEmpty } from 'ol/extent';
|
import { isEmpty } from 'ol/extent';
|
||||||
import MouseWheelZoom from 'ol/interaction/MouseWheelZoom';
|
import MouseWheelZoom from 'ol/interaction/MouseWheelZoom';
|
||||||
|
import TileLayer from 'ol/layer/Tile';
|
||||||
import { fromLonLat, transformExtent } from 'ol/proj';
|
import { fromLonLat, transformExtent } from 'ol/proj';
|
||||||
import { Component, ReactNode } from 'react';
|
import { Component, ReactNode } from 'react';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
@@ -30,7 +31,7 @@ import { MeasureVectorLayer } from './components/MeasureVectorLayer';
|
|||||||
import { GeomapHoverPayload } from './event';
|
import { GeomapHoverPayload } from './event';
|
||||||
import { getGlobalStyles } from './globalStyles';
|
import { getGlobalStyles } from './globalStyles';
|
||||||
import { defaultMarkersConfig } from './layers/data/markersLayer';
|
import { defaultMarkersConfig } from './layers/data/markersLayer';
|
||||||
import { DEFAULT_BASEMAP_CONFIG } from './layers/registry';
|
import { DEFAULT_BASEMAP_CONFIG, geomapLayerRegistry } from './layers/registry';
|
||||||
import { ControlsOptions, Options, MapLayerState, MapViewConfig, TooltipMode } from './types';
|
import { ControlsOptions, Options, MapLayerState, MapViewConfig, TooltipMode } from './types';
|
||||||
import { getActions } from './utils/actions';
|
import { getActions } from './utils/actions';
|
||||||
import { getLayersExtent } from './utils/getLayersExtent';
|
import { getLayersExtent } from './utils/getLayersExtent';
|
||||||
@@ -396,7 +397,45 @@ export class GeomapPanel extends Component<Props, State> {
|
|||||||
|
|
||||||
this.mouseWheelZoom?.setActive(Boolean(options.mouseWheelZoom));
|
this.mouseWheelZoom?.setActive(Boolean(options.mouseWheelZoom));
|
||||||
|
|
||||||
if (options.showAttribution) {
|
// Handle attribution visibility per layer based on required vs optional
|
||||||
|
let hasAnyAttribution = false;
|
||||||
|
|
||||||
|
for (const layerState of this.layers) {
|
||||||
|
const layerType = layerState.options.type;
|
||||||
|
const layerRegistryItem = layerType ? geomapLayerRegistry.getIfExists(layerType) : null;
|
||||||
|
const requiresAttribution = layerRegistryItem?.requiresAttribution === true;
|
||||||
|
|
||||||
|
// Check if this layer's source has attribution capability
|
||||||
|
const layer = layerState.layer;
|
||||||
|
if (layer instanceof TileLayer) {
|
||||||
|
const source = layer.getSource();
|
||||||
|
if (source && typeof source.getAttributions === 'function') {
|
||||||
|
const currentAttributions = source.getAttributions();
|
||||||
|
|
||||||
|
// Store original attribution if not already stored
|
||||||
|
if (!layerState.originalAttribution && currentAttributions) {
|
||||||
|
layerState.originalAttribution = currentAttributions;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show attribution if it's required OR if user has enabled optional attributions
|
||||||
|
if (requiresAttribution || options.showAttribution) {
|
||||||
|
// Restore original attribution if we had cleared it
|
||||||
|
if (layerState.originalAttribution && typeof source.setAttributions === 'function') {
|
||||||
|
source.setAttributions(layerState.originalAttribution);
|
||||||
|
}
|
||||||
|
hasAnyAttribution = true;
|
||||||
|
} else {
|
||||||
|
// Hide optional attribution by clearing it
|
||||||
|
if (typeof source.setAttributions === 'function') {
|
||||||
|
source.setAttributions(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add attribution control if there are any attributions to show
|
||||||
|
if (hasAnyAttribution) {
|
||||||
this.map.addControl(new Attribution({ collapsed: true, collapsible: true }));
|
this.map.addControl(new Attribution({ collapsed: true, collapsible: true }));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ export const carto: MapLayerRegistryItem<CartoConfig> = {
|
|||||||
name: 'CARTO basemap',
|
name: 'CARTO basemap',
|
||||||
description: 'Add layer CARTO Raster basemaps',
|
description: 'Add layer CARTO Raster basemaps',
|
||||||
isBaseMap: true,
|
isBaseMap: true,
|
||||||
|
requiresAttribution: true,
|
||||||
defaultOptions: defaultCartoConfig,
|
defaultOptions: defaultCartoConfig,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ export const standard: MapLayerRegistryItem = {
|
|||||||
name: 'Open Street Map',
|
name: 'Open Street Map',
|
||||||
description: 'Add map from a collaborative free geographic world database',
|
description: 'Add map from a collaborative free geographic world database',
|
||||||
isBaseMap: true,
|
isBaseMap: true,
|
||||||
|
requiresAttribution: true,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function that configures transformation and returns a transformer
|
* Function that configures transformation and returns a transformer
|
||||||
|
|||||||
@@ -26,6 +26,8 @@ export const defaultBaseLayer: MapLayerRegistryItem = {
|
|||||||
id: DEFAULT_BASEMAP_CONFIG.type,
|
id: DEFAULT_BASEMAP_CONFIG.type,
|
||||||
name: 'Default base layer',
|
name: 'Default base layer',
|
||||||
isBaseMap: true,
|
isBaseMap: true,
|
||||||
|
// Default uses CARTO which requires attribution
|
||||||
|
requiresAttribution: true,
|
||||||
|
|
||||||
create: (map: OpenLayersMap, options: MapLayerOptions, eventBus: EventBus, theme: GrafanaTheme2) => {
|
create: (map: OpenLayersMap, options: MapLayerOptions, eventBus: EventBus, theme: GrafanaTheme2) => {
|
||||||
const serverLayerType = config?.geomapDefaultBaseLayerConfig?.type;
|
const serverLayerType = config?.geomapDefaultBaseLayerConfig?.type;
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import { GeomapPanel } from './GeomapPanel';
|
|||||||
import { LayersEditor } from './editor/LayersEditor';
|
import { LayersEditor } from './editor/LayersEditor';
|
||||||
import { MapViewEditor } from './editor/MapViewEditor';
|
import { MapViewEditor } from './editor/MapViewEditor';
|
||||||
import { getLayerEditor } from './editor/layerEditor';
|
import { getLayerEditor } from './editor/layerEditor';
|
||||||
|
import { geomapLayerRegistry } from './layers/registry';
|
||||||
import { mapPanelChangedHandler, mapMigrationHandler } from './migrations';
|
import { mapPanelChangedHandler, mapMigrationHandler } from './migrations';
|
||||||
import { defaultMapViewConfig, Options, TooltipMode, GeomapInstanceState } from './types';
|
import { defaultMapViewConfig, Options, TooltipMode, GeomapInstanceState } from './types';
|
||||||
|
|
||||||
@@ -105,6 +106,17 @@ export const plugin = new PanelPlugin<Options>(GeomapPanel)
|
|||||||
|
|
||||||
// The controls section
|
// The controls section
|
||||||
category = [t('geomap.category-map-controls', 'Map controls')];
|
category = [t('geomap.category-map-controls', 'Map controls')];
|
||||||
|
|
||||||
|
// Check if any layer requires attribution
|
||||||
|
const requiresAttribution = state?.layers?.some((layerState) => {
|
||||||
|
const layerType = layerState.options.type;
|
||||||
|
if (layerType) {
|
||||||
|
const layerRegistryItem = geomapLayerRegistry.getIfExists(layerType);
|
||||||
|
return layerRegistryItem?.requiresAttribution === true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
builder
|
builder
|
||||||
.addBooleanSwitch({
|
.addBooleanSwitch({
|
||||||
category,
|
category,
|
||||||
@@ -123,11 +135,15 @@ export const plugin = new PanelPlugin<Options>(GeomapPanel)
|
|||||||
.addBooleanSwitch({
|
.addBooleanSwitch({
|
||||||
category,
|
category,
|
||||||
path: 'controls.showAttribution',
|
path: 'controls.showAttribution',
|
||||||
name: t('geomap.name-show-attribution', 'Show attribution'),
|
name: requiresAttribution
|
||||||
description: t(
|
? t('geomap.name-show-optional-attribution', 'Show optional attribution')
|
||||||
'geomap.description-show-attribution',
|
: t('geomap.name-show-attribution', 'Show attribution'),
|
||||||
'Show the map source attribution info in the lower right'
|
description: requiresAttribution
|
||||||
),
|
? t(
|
||||||
|
'geomap.description-show-optional-attribution',
|
||||||
|
'Required attributions are always shown. Toggle this to also show attribution from other layers at your discretion.'
|
||||||
|
)
|
||||||
|
: t('geomap.description-show-attribution', 'Show the map source attribution info in the lower right'),
|
||||||
defaultValue: true,
|
defaultValue: true,
|
||||||
})
|
})
|
||||||
.addBooleanSwitch({
|
.addBooleanSwitch({
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ export interface MapLayerState<TConfig = unknown> extends LayerElement {
|
|||||||
onChange: (cfg: MapLayerOptions<TConfig>) => void;
|
onChange: (cfg: MapLayerOptions<TConfig>) => void;
|
||||||
isBasemap?: boolean;
|
isBasemap?: boolean;
|
||||||
mouseEvents: Subject<FeatureLike | undefined>;
|
mouseEvents: Subject<FeatureLike | undefined>;
|
||||||
|
originalAttribution?: any; // Store original attribution to restore when showing optional attributions
|
||||||
}
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
|
|||||||
Reference in New Issue
Block a user