如何在d3.js地球标记上增加高度,模仿图钉?

问题描述

在地球仪上放置标记时,标记会平放在表面上。

虽然标记旋转出视线可能会造成麻烦;有没有办法给这个标记高度?

我试图在针上得到一个点,而不是地球表面上的点,伸出一点到地球表面上方。

不是这样的:

-----o-----

但这是

     o
_____|_____

模仿其中之一:

Push-pin needle

当前标记绘制如下:

const width = 220;
const height = 220;
const config = {
  speed: 0.025,verticalTilt: 10,horizontalTilt: -10
}
let locations = [];
const svg = d3.select('svg')
  .attr('width',width).attr('height',height);
const markerGroup = svg.append('g');
const projection = d3.geoOrthographic();
const initialScale = projection.scale(99.5).translate([100,100]);
const path = d3.geoPath().projection(projection);
const center = [width / 2,height / 2];

drawGlobe();
drawGraticule();
enableRotation();

const locationData = [
    {"latitude": -33.8688,"longitude": 151.2093}
];

function drawGlobe() {
  d3.queue()
    .defer(d3.json,'https://raw.githubusercontent.com/cszang/dendroBox/master/data/world-110m2.json')
    .await((error,worldData) => {
      svg.selectAll(".segment")
        .data(topojson.feature(worldData,worldData.objects.countries).features)
        .enter().append("path")
        .attr("class","segment")
        .attr("d",path)
        .style("stroke","silver")
        .style("stroke-width","1px")
        .style("fill",(d,i) => 'silver')
        .style("opacity",".5");
      locations = locationData;
      drawMarkers();
    });
}

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

  svg.append("path")
    .datum(graticule)
    .attr("class","graticule")
    .attr("d",path)
    .style("fill","#fff")
    .style("stroke","#ececec");
}

function enableRotation() {
  d3.timer(function(elapsed) {
    projection.rotate([config.speed * elapsed - 120,config.verticalTilt,config.horizontalTilt]);
    svg.selectAll("path").attr("d",path);
    drawMarkers();
  });
}

function drawMarkers() {
  const markers = markerGroup.selectAll('circle')
    .data(locations);
  markers
    .enter()
    .append('circle')
    .merge(markers)
    .attr('cx',d => projection([d.longitude,d.latitude])[0])
    .attr('cy',d.latitude])[1])
    .attr('fill',d => {
      const coordinate = [d.longitude,d.latitude];
      gdistance = d3.geodistance(coordinate,projection.invert(center));
      return gdistance > 1.55 ? 'none' : 'tomato';
    })

    // 1.57

    .attr('r',3);

  markerGroup.each(function() {
    this.parentNode.appendChild(this);
  });
}
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://d3js.org/topojson.v1.min.js"></script>
<svg></svg>

解决方法

使用this answer作为灵感,您可以创建第二个投影,该投影与第一个投影相同,但是具有更大的scale值。这将直接在地球上的实际点上上方投射一个点,就像它悬在其上方一样。这使您可以从地面开始绘制一条线,并从各个角度查看它。它甚至可以与您的隐藏标记逻辑一起使用。

const width = 220;
const height = 220;
const config = {
  speed: 0.025,verticalTilt: 10,horizontalTilt: -10
}
let locations = [];
const svg = d3.select('svg')
  .attr('width',width).attr('height',height);
const markerGroup = svg.append('g');
const projection = d3.geoOrthographic()
  .scale(99.5)
  .translate([100,100]);
const markerProjection = d3.geoOrthographic()
  .scale(108)
  .translate(projection.translate());

const path = d3.geoPath().projection(projection);
const center = [width / 2,height / 2];

drawGlobe();
drawGraticule();
enableRotation();

const locationData = [
    {"latitude": -33.8688,"longitude": 151.2093}
];

function drawGlobe() {
  d3.queue()
    .defer(d3.json,'https://raw.githubusercontent.com/cszang/dendrobox/master/data/world-110m2.json')
    .await((error,worldData) => {
      svg.selectAll(".segment")
        .data(topojson.feature(worldData,worldData.objects.countries).features)
        .enter().append("path")
        .attr("class","segment")
        .attr("d",path)
        .style("stroke","silver")
        .style("stroke-width","1px")
        .style("fill",(d,i) => 'silver')
        .style("opacity",".5");
      locations = locationData;
      drawMarkers();
    });
}

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

  svg.append("path")
    .datum(graticule)
    .attr("class","graticule")
    .attr("d",path)
    .style("fill","#fff")
    .style("stroke","#ececec");
}

function enableRotation() {
  d3.timer(function(elapsed) {
    projection.rotate([config.speed * elapsed - 120,config.verticalTilt,config.horizontalTilt]);
    markerProjection.rotate(projection.rotate());
    svg.selectAll("path").attr("d",path);
    drawMarkers();
  });
}

function drawMarkers() {
  const markers = markerGroup.selectAll('.marker')
    .data(locations);
  const newMarkers = markers
    .enter()
    .append('g')
    .attr('class','marker')
  
  newMarkers.append("line");
  
  newMarkers.append("circle")
    .attr("r",3);
  
  newMarkers.merge(markers)
    .selectAll("line")
    .attr("x1",d => projection([d.longitude,d.latitude])[0])
    .attr("y1",d.latitude])[1])
    .attr("x2",d => markerProjection([d.longitude,d.latitude])[0])
    .attr("y2",d.latitude])[1])
    .attr('stroke',d => {
      const coordinate = [d.longitude,d.latitude];
      gdistance = d3.geoDistance(coordinate,markerProjection.invert(center));
      return gdistance > (Math.PI / 2) ? 'none' : 'black';
    })
  
  newMarkers
    .merge(markers)
    .selectAll("circle")
    .attr('cx',d.latitude])[0])
    .attr('cy',d.latitude])[1])
    .attr('fill',markerProjection.invert(center));
      return gdistance > (Math.PI / 2) ? 'none' : 'tomato';
    })

  markerGroup.each(function() {
    this.parentNode.appendChild(this);
  });
}
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://d3js.org/topojson.v1.min.js"></script>
<svg></svg>

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...