Power BI is turning 10! Tune in for a special live episode on July 24 with behind-the-scenes stories, product evolution highlights, and a sneak peek at what’s in store for the future.
Save the dateJoin us for an expert-led overview of the tools and concepts you'll need to become a Certified Power BI Data Analyst and pass exam PL-300. Register now.
I am trying to configure Leaflet to work with pbiviz but it fails to load the tiles due to CSP policy "Refused to load the image '<URL>' because it violates the following Content Security Policy directive: "default-src <URL> data: blob: 'unsafe-inline' 'unsafe-eval'". Note that 'img-src' was not explicitly set, so 'default-src' is used as a fallback."
All examples of leaflet implementation on github use earlier API. Has anyone got a minimal reprex using latest API and latest Leaflet. Or alternative, is there anyway to set the CSP with pbiviz to allow the tiles to load?
All works excepts tile loading.
I am running the visual in debug mode
{
"visual": {
"name": "pbiBubble2",
"displayName": "pbiBubble2",
"guid": "pbiBubble29A693D8F658E46768CC5CB9E2D6716F6",
"visualClassName": "Visual",
"version": "1.0.0.0",
"description": "",
"supportUrl": "",
"gitHubUrl": ""
},
"apiVersion": "5.3.0",
"author": {
"name": "name",
"email": "name@gmail.com"
},
"assets": {
"icon": "assets/icon.png"
},
"style": "style/visual.less",
"capabilities": "capabilities.json",
"dependencies": null,
"stringResources": [],
"version": "1.0.0.0"
}
"use strict";
import "./../style/visual.less";
import "./../node_modules/leaflet/dist/leaflet.css";
import powerbi from "powerbi-visuals-api";
import { FormattingSettingsService } from "powerbi-visuals-utils-formattingmodel";
import * as d3 from "d3";
import { ArrayLike, color, Color, ColorCommonInstance, ColorFactory, ColorSpaceObject, csvParseRows, D3ZoomEvent, hierarchy, HierarchyCircularNode, HierarchyNode, Numeric, scaleOrdinal, ScaleThreshold, ZoomBehavior, ZoomTransform } from "d3";
type Selection<T extends d3.BaseType> = d3.Selection<T, any, any, any>;
import VisualConstructorOptions = powerbi.extensibility.visual.VisualConstructorOptions;
import VisualUpdateOptions = powerbi.extensibility.visual.VisualUpdateOptions;
import IVisual = powerbi.extensibility.visual.IVisual;
import ISelectionManager = powerbi.extensibility.ISelectionManager;
import DataView = powerbi.DataView;
import { VisualFormattingSettingsModel } from "./settings";
import * as L from "leaflet";
export class Visual implements IVisual {
private target: HTMLElement; // Root element
private map: L.Map;
private Tooltip: Selection<HTMLElement>; // Tooltip
private svgroot: Selection<SVGElement>; // svg
private graph: Selection<SVGElement>; // Graph group
private formattingSettings: VisualFormattingSettingsModel;
private formattingSettingsService: FormattingSettingsService;
private selectionManager: ISelectionManager;
constructor(options: VisualConstructorOptions) {
console.log('Visual constructor', options);
this.formattingSettingsService = new FormattingSettingsService();
this.target = options.element;
// Fill the target element with the Leaflet map
this.target.style.width = "100%";
this.target.style.height = "100%";
// Base maps
var Esri = L.tileLayer("https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}", {attribution: 'ESRI', minZoom: 1, maxZoom: 20});
var osm = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {attribution: '© OSM', minZoom: 1, maxZoom: 20});
var topo = L.tileLayer('https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png', {attribution: '© OpenTopoMap', minZoom: 1, maxZoom: 20});
var white = L.tileLayer("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQAAAAEAAQMAAABmvDolAAAAA1BMVEX///+nxBvIAAAAH0lEQVQYGe3BAQ0AAADCIPunfg43YAAAAAAAAAAA5wIhAAAB9aK9BAAAAABJRU5ErkJggg==");
var localm = L.tileLayer('./basemaps//{z}/{x}/{y}.png', {tms: false, attribution: 'Local Map'});
// Overlay layers
var lyr = L.tileLayer('./basemaps/local_tiles/{z}/{x}/{y}.png', {tms: true, opacity: 8.0, attribution: "Local Map Layer"});
var basemaps = {"OpenStreetMap": osm, "Esri Map": Esri, "OpenTopoMap":topo, "Without background": white, "Local": localm}
var overlaymaps = {"Local": lyr}
if (typeof document !== "undefined") {
this.map = L.map(this.target, {
center: [-32.3800067213876, 150.94998609762376],
crs:L.CRS.EPSG3857,
zoom: 13,
minZoom: 1,
maxZoom: 20,
layers: [topo]});
L.control.layers(basemaps, overlaymaps, {collapsed: true}).addTo(this.map);
// Fit to overlay bounds (SW and NE points with (lat, lon))
//this.map.fitBounds([[-32.4500134427752, 151.04997219524756], [-32.31, 150.84999999999997]]);
}
}
public update(options: VisualUpdateOptions) {
this.formattingSettings = this.formattingSettingsService.populateFormattingSettingsModel(VisualFormattingSettingsModel, options.dataViews[0]);
console.log('Visual update', options);
if (this.svgroot) {
this.svgroot.append('text').html('Here we go').attr('x',50).attr('y',50);
}
}
/**
* Returns properties pane formatting model content hierarchies, properties and latest formatting values, Then populate properties pane.
* This method is called once every time we open properties pane or when the user edit any format property.
*/
public getFormattingModel(): powerbi.visuals.FormattingModel {
return this.formattingSettingsService.buildFormattingModel(this.formattingSettings);
}
}
After lot of head scratching, I found a workaround that is to set the WebAccess tag in capabilities.json. Though this does not change the img-src tag of the CSP policy and may not be the most secure way to load tiles from an external source. The settings I used are as below:
"privileges": [{
"name": "WebAccess",
"essential": true,
"parameters": [ "https://*.opentopomap.org", "https://*.openstreetmap.org", "https://*.arcgisonline.com", "https://*.stadiamaps.com"]
}]
Check out the July 2025 Power BI update to learn about new features.
This is your chance to engage directly with the engineering team behind Fabric and Power BI. Share your experiences and shape the future.
User | Count |
---|---|
7 | |
7 | |
3 | |
2 | |
2 |
User | Count |
---|---|
6 | |
5 | |
4 | |
4 | |
4 |