Skip to content
Advertisement

Implementing a Bl.ocks.org graph into a Vue.js component

I’m a D3 beginner and I want to use this d3 element into my Vue.js component. The problem here is that the periodic rotation I need does not work. It starts looping errors of null on the element projection that is globally defined. It seems that the first time works but in the second one the object is no longer defined.

Here’s the code:

<template>
    <div>
        <svg></svg>
    </div>
</template>

<script>
import * as d3 from "d3";
import { feature } from "topojson-client";
import { json } from "d3-fetch";
export default {
    name: "WorldCases",
    data: function() {
        return {
            svg: null,
            markerGroup: null,
            projection: null,
            initialScale: null,
            path: null,
            center: null,
            config: null,
            locations: [
                { latitude: 22, longitude: 88 },
                { latitude: 12.61315, longitude: 38.37723 },
                { latitude: -30, longitude: -58 },
                { latitude: -14.270972, longitude: -170.132217 },
                { latitude: 28.033886, longitude: 1.659626 },
                { latitude: 40.463667, longitude: -3.74922 },
                { latitude: 35.907757, longitude: 127.766922 },
                { latitude: 23.634501, longitude: -102.552784 },
            ],
        };
    },
    mounted() {
        this.graph();
    },
    methods: {
        graph() {
            const width = 960;
            const height = 500;
            this.config = {
                speed: 0.005,
                verticalTilt: -30,
                horizontalTilt: 0,
            };
            this.svg = d3
                .select("svg")
                .attr("width", width)
                .attr("height", height);
            this.markerGroup = this.svg.append("g");
            this.projection = d3.geoOrthographic();
            console.log(this.projection);
            this.initialScale = this.projection.scale();
            this.path = d3.geoPath().projection(this.projection);
            this.center = [width / 2, height / 2];

            this.drawGlobe();
            this.drawGraticule();

            d3.timer(function(elapsed) {
                this.projection.rotate([
                    this.config.speed * elapsed - 120,
                    this.config.verticalTilt,
                    this.config.horizontalTilt,
                ]);
                this.svg.selectAll("path").attr("d", this.path);
                this.drawMarkers();
            }); 
        },
        drawGlobe() {
            Promise.all([json("./world-110m.json")]).then(([worldData, error]) => {
                // Do your stuff. Content of both files is now available in stations and svg
                this.svg
                    .selectAll(".segment")
                    .data(feature(worldData, worldData.objects.countries).features)
                    .enter()
                    .append("path")
                    .attr("class", "segment")
                    .attr("d", this.path)
                    .style("stroke", "#888")
                    .style("stroke-width", "1px")
                    .style("fill", (d, i) => "#e5e5e5")
                    .style("opacity", ".6");
                this.drawMarkers();
            });
        },
        drawMarkers() {
            const markers = this.markerGroup.selectAll("circle").data(this.locations);
            markers
                .enter()
                .append("circle")
                .merge(markers)
                .attr("cx", (d) => this.projection([d.longitude, d.latitude])[0])
                .attr("cy", (d) => this.projection([d.longitude, d.latitude])[1])
                .attr("fill", (d) => {
                    const coordinate = [d.longitude, d.latitude];
                    var gdistance = d3.geoDistance(coordinate, this.projection.invert(this.center));
                    return gdistance > 1.57 ? "none" : "steelblue";
                })
                .attr("r", 7);

            this.markerGroup.each(function() {
                this.parentNode.appendChild(this);
            });
        },

        drawGraticule() {
            const graticule = d3.geoGraticule().step([10, 10]);

            this.svg
                .append("path")
                .datum(graticule)
                .attr("class", "graticule")
                .attr("d", this.path)
                .style("fill", "#fff")
                .style("stroke", "#ccc");
        },
    },
};
</script>

<style></style>

The error is represented in the following image:

error pic

How can I fix it ? In this configuration it does not work.

Thank you

Advertisement

Answer

I solved my problem remembering that in Javascript sometimes “this” element is not always what we expect when we work inside other functions.

So what I needed to do was to save the “this” object in a variable and using that variable to do what I needed:

var x = this;
d3.timer(function(elapsed) {
            x.projection.rotate([
                x.config.speed * elapsed - 120,
                x.config.verticalTilt,
                x.config.horizontalTilt,
            ]);
            x.svg.selectAll("path").attr("d", this.path);
            x.drawMarkers();
        });
User contributions licensed under: CC BY-SA
7 People found this is helpful
Advertisement