cancel
Showing results for 
Search instead for 
Did you mean: 
Reply
satishr
Helper III
Helper III

Need help getting D3 charts on Leaflet map

I'm creating a custom visual which requires doughnut chart to be displayed over a map. To start with I'm trying to replicate this.

http://bl.ocks.org/d3noob/9267535

 

It has circles over a leaflet map. I got the leaflet map working but D3 part doesn't work. Please help me with this. If I get the circles on the map I can later replace them with doughnuts.

 

Visual.ts

module powerbi.extensibility.visual {
    
    
    export interface Data{
        lat: number;
        lng: number;
        latlng: {};
        status: string[];
        itemCnt: number[];
    }

    export class Visual implements IVisual {
        private target: HTMLElement;
        private divMap: HTMLElement;
        private divTable: HTMLElement;
        private map: L.Map;
        private basemap: L.TileLayer;
        private layer: L.TileLayer;

        constructor(options: VisualConstructorOptions) {
            console.log('Visual constructor', options);
            this.target = options.element;
            this.divMap = document.createElement("div");
            this.divMap.id = "map";
            this.divMap.style.height = "100%";   
            this.divMap.style.width = "100%";
            options.element.appendChild(this.divMap);

            var L = typeof L !== 'undefined' ? L : window['L'];
            
            this.map = L.map('map');
            this.map.setView([-41.28,174.77], 11); 
            var mapLink = '<a href="http://openstreetmap.org">OpenStreetMap</a>';
            this.layer = L.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { attribution: '&copy; ' + mapLink + ' Contributors', maxZoom: 18, });
            this.map.addLayer(this.layer);
        }


        public update(options: VisualUpdateOptions) {
            //update map size
            this.divMap.style.height = options.viewport.height.toString() + "px";
            this.divMap.style.width = options.viewport.width.toString() + "px";
            
            var svg = d3.select("#map").select("svg"),
            g = svg.append("g");
            
            var data: Data[] = [ 
                {lat:-41.28, lng:174.77, latlng:{}, status:['Under','Over','Normal'], itemCnt: [30,40,50]},
                {lat:-41.29, lng:174.76, latlng:{}, status:['Under','Over','Normal'], itemCnt: [30,30,60]},
                {lat:-41.23, lng:174.79, latlng:{}, status:['Under','Over','Normal'], itemCnt: [30,70,10]} 
            ];

            data.forEach(function(d) {
                d.latlng = new L.LatLng(<number>d.lat, <number>d.lng);
            })
            
            var circle= g.selectAll("circle")
                .data(data)
                .enter().append("circle")
                .style("stroke", "black")  
                .style("opacity", .6) 
                .style("fill", "red")
                .attr("r", 20)
                
            this.map.on("viewreset", update);
            update();

            function update() {
                circle.attr("transform", 
                function(d)  { 
                        return "translate("+ 
                        this.map.latLngToLayerPoint(new L.LatLng(d.lat, d.lng)).x +","+ 
                        this.map.latLngToLayerPoint(new L.LatLng(d.lat, d.lng)).y +")";
                        }
                )
            }
        }


    }
}

Visual.less

@import (less) "node_modules/leaflet/dist/leaflet.css";

Package.JSON

{
  "name": "visual",
  "version": "1.1",
  "dependencies": {
    "@types/d3": "^3.5.40",
    "d3": "^3.5.17",
    "geojson": "^0.5.0",
    "leaflet": "^1.3.1",
    "powerbi-visuals-utils-dataviewutils": "1.2.0"
  },
  "devDependencies": {
    "typescript": "^2.6.2"
  }
}

pbiviz.JSON

{
  "visual": {
    "name": "customMap",
    "displayName": "Custom Map",
    "guid": "custommap5A89ADFB5F694F71AB6D00C94383BB2B",
    "visualClassName": "Visual",
    "version": "1.0.0",
    "description": "",
    "supportUrl": "",
    "gitHubUrl": ""
  },
  "apiVersion": "1.10.0",
  "author": {
    "name": "",
    "email": ""
  },
  "assets": {
    "icon": "assets/icon.png"
  },
  "externalJS": [
    "node_modules/powerbi-visuals-utils-dataviewutils/lib/index.js",
    "node_modules/d3/d3.min.js",
    "node_modules/leaflet/dist/leaflet.js"
  ],
  "style": "style/visual.less",
  "capabilities": "capabilities.json",
  "dependencies": "dependencies.json",
  "stringResources": []
}
2 ACCEPTED SOLUTIONS
v-viig
Community Champion
Community Champion

We replied with the fixed version of your code.

 

Could you please post the fixed code here if you are ok with sharing?

 

Ignat Vilesov,

Software Engineer

 

Microsoft Power BI Custom Visuals

pbicvsupport@microsoft.com

View solution in original post

Thanks, @v-viig. You fix works. Here is the fixed visual.ts.

 

module powerbi.extensibility.visual {


    export interface Data {
        lat: number;
        lng: number;
        latlng: {};
        status: string[];
        itemCnt: number[];
    }

    var L = typeof L !== 'undefined' ? L : window['L'];

    export class Visual implements IVisual {
        private target: HTMLElement;
        private divMap: HTMLElement;
        private divTable: HTMLElement;
        private map: L.Map;
        private basemap: L.TileLayer;
        private layer: L.TileLayer;

        constructor(options: VisualConstructorOptions) {
            console.log('Visual constructor', options);
            this.target = options.element;
            this.divMap = document.createElement("div");
            this.divMap.id = "map";
            this.divMap.style.height = "100%";
            this.divMap.style.width = "100%";
            options.element.appendChild(this.divMap);

            this.map = L.map('map');
            this.map.setView([-41.28, 174.77], 11);
            var mapLink = '<a href="http://openstreetmap.org">OpenStreetMap</a>';
            this.layer = L.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { attribution: '&copy; ' + mapLink + ' Contributors', maxZoom: 18, });
            this.map.addLayer(this.layer);

            L.svg().addTo(this.map);
        }


        public update(options: VisualUpdateOptions) {
            debugger;
            //update map size
            this.divMap.style.height = options.viewport.height.toString() + "px";
            this.divMap.style.width = options.viewport.width.toString() + "px";

            var svg = d3.select("#map").select("svg"),
                g = svg.append("g");

            var data: Data[] = [
                { lat: -41.28, lng: 174.77, latlng: {}, status: ['Under', 'Over', 'Normal'], itemCnt: [30, 40, 50] },
                { lat: -41.29, lng: 174.76, latlng: {}, status: ['Under', 'Over', 'Normal'], itemCnt: [30, 30, 60] },
                { lat: -41.23, lng: 174.79, latlng: {}, status: ['Under', 'Over', 'Normal'], itemCnt: [30, 70, 10] }
            ];

            data.forEach(function (d) {
                d.latlng = new L.LatLng(<number>d.lat, <number>d.lng);
            })

            var circle = g.selectAll("circle")
                .data(data)
                .enter().append("circle")
                .style("stroke", "black")
                .style("opacity", .6)
                .style("fill", "red")
                .attr("r", 20)

            const map = this.map;

            function update() {
                circle.attr("transform",
                    function (d) {
                        return "translate(" +
                            map.latLngToLayerPoint(d.latlng as any).x + "," +
                            map.latLngToLayerPoint(d.latlng as any).y + ")";
                    }
                )
            }

            this.map.on("viewreset", update);
this.map.on("zoom", update); update(); } } }

View solution in original post

17 REPLIES 17

Helpful resources

Announcements
PBI User Groups

Welcome to the User Group Public Preview

Check out new user group experience and if you are a leader please create your group!

MBAS on Demand

Microsoft Business Applications Summit sessions

On-demand access to all the great content presented by the product teams and community members! #MSBizAppsSummit #CommunityRocks

MBAS Attendee Badge

Claim Your Badge & Digital Swag!

Check out how to claim yours today!

secondImage

Are You Ready?

Test your skills now with the Cloud Skills Challenge.

Top Solution Authors