Forum Discussion

satishr's avatar
satishr
Helper III
8 years ago
Solved

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": []
}
  • 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

    [email protected]

  • 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(); } } }

17 Replies

  • v-viig's avatar
    v-viig
    Community Champion

    What exactly issue have you faced? Can you sahre source code as a zip file?

     

    Ignat Vilesov,

    Software Engineer

     

    Microsoft Power BI Custom Visuals

    [email protected]

    • satishr's avatar
      satishr
      Helper III

      I'm trying to create D3 circles on a leaflet map for a custom visual. I'm able to get the map but the D3 circles are not working. I have sent you my code. 

      • v-viig's avatar
        v-viig
        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

        [email protected]