Skip to main content
cancel
Showing results for 
Search instead for 
Did you mean: 

Compete to become Power BI Data Viz World Champion! First round ends August 18th. Get started.

Reply
HerveB
Regular Visitor

using Leaflet in power Bi with pbiviz API 5.3.0

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: '&copy; OSM', minZoom: 1, maxZoom: 20});
        var topo = L.tileLayer('https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png', {attribution: '&copy; OpenTopoMap', minZoom: 1, maxZoom: 20});
        var white = L.tileLayer("data&colon;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);
    }
}

 

 

 

 

1 REPLY 1
HerveB
Regular Visitor

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"]
    }]

  

Helpful resources

Announcements
July 2025 community update carousel

Fabric Community Update - July 2025

Find out what's new and trending in the Fabric community.

July PBI25 Carousel

Power BI Monthly Update - July 2025

Check out the July 2025 Power BI update to learn about new features.