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