D3 картография: длинные / длинные круги на неправильном месте на карте - PullRequest
0 голосов
/ 01 января 2019

У меня вопрос по картографии D3.Я работаю над небольшим проектом, и я новичок в D3.

Я начал с этого примера: http://bl.ocks.org/mbostock/5914438 Вместо показа сетки состояний я хотел бы показать круги накарта в определенных местах (lon / lat).В настоящее время я сталкиваюсь с проблемой, заключающейся в том, что круги не находятся в правильных местах на карте.Я подозреваю, что проблема заключается в специальной проекции, которую использует Майк.Он использует квадратную проекцию 1x1.Вероятно, это необходимо для отображения плитки.Когда я проецирую координаты, все значения находятся в диапазоне от -1 до 1. Я думал, что смогу исправить это, умножив его на ширину, высоту и ширину, но это не сработало.Ниже приведен мой код (фрагмент не запускается, потому что отсутствует файл).Спасибо за помощь!

<!DOCTYPE html>
<meta charset="utf-8">
<style>

body {
  margin: 0;
}

path {
  fill: none;
  stroke: red;
  stroke-linejoin: round;
  stroke-width: 1.5px;
}

circle {
  fill: #fff;
  fill-opacity: 0.4;
  stroke: #111;
}

</style>
<svg>
</svg>
<script src="//d3js.org/d3.v4.min.js"></script>
<script src="//d3js.org/d3-tile.v0.0.min.js"></script>
<script src="//d3js.org/topojson.v1.min.js"></script>
<script>
var pi = Math.PI,
    tau = 2 * pi;

var width = Math.max(960, window.innerWidth),
    height = Math.max(500, window.innerHeight);

// Initialize the projection to fit the world in a 1×1 square centered at the origin.
var projection = d3.geoMercator()
    .scale(1 / tau)
    .translate([0, 0]);

var path = d3.geoPath()
    .projection(projection);

var tile = d3.tile()
    .size([width, height]);

var zoom = d3.zoom()
    .scaleExtent([1 << 9, 1 << 23])
    .on("zoom", zoomed);

var svg = d3.select("svg")
    .attr("width", width)
    .attr("height", height);

var raster = svg.append("g");

var vector = svg.append("path");

var circle = svg.append("g")
      
d3.json("/data/flyingsites/AD.json", function(error, flyingsites) {
  if (error) console.log(error); 
  
  // Compute the projected initial center.
  var center = projection([6.2, 45.8]);//45,809718, 6,252314

  // Apply a zoom transform equivalent to projection.{scale,translate,center}.
  svg
      .call(zoom)
      .call(zoom.transform, d3.zoomIdentity
          .translate(width / 2, height / 2)
          .scale(1 << 12)
          .translate(-center[0], -center[1]));

//add flying sites
  circle.selectAll("circle")
  	.data(flyingsites.features)
  .enter().append("circle")
  	.attr('r',5)
    .attr('cx',function(d) { return projection(d.geometry.coordinates)[0]*width})
    .attr('cy',function(d) { return projection(d.geometry.coordinates)[1]*height})
    .style('fill','red')
    
//console.log(flyingsites.features);
//console.log(circle);
});


function zoomed() {
  var transform = d3.event.transform;

  var tiles = tile
      .scale(transform.k)
      .translate([transform.x, transform.y])
      ();

  vector
      .attr("transform", transform)
      .style("stroke-width", 1 / transform.k);
     
  circle
      .attr("transform", "translate(" + transform.x + "," + transform.y + ")");
      
  var image = raster
      .attr("transform", stringify(tiles.scale, tiles.translate))
    .selectAll("image")
    .data(tiles, function(d) { return d; });

  image.exit().remove();

  image.enter().append("image")
      .attr("xlink:href", function(d) { return "http://" + "abc"[d[1] % 3] + ".tile.openstreetmap.org/" + d[2] + "/" + d[0] + "/" + d[1] + ".png"; })
      .attr("x", function(d) { return d[0] * 256; })
      .attr("y", function(d) { return d[1] * 256; })
      .attr("width", 256)
      .attr("height", 256);
}

function stringify(scale, translate) {
  var k = scale / 256, r = scale % 1 ? Number : Math.round;
  return "translate(" + r(translate[0] * scale) + "," + r(translate[1] * scale) + ") scale(" + k + ")";
}

</script>

1 Ответ

0 голосов
/ 01 января 2019

Подход, который вы используете, не сработает, например, он не учитывает масштаб (просто переведите).Это очень важно, так как d3-мозаика использует геометрическое масштабирование - она ​​применяет преобразование масштабирования (масштабирование и преобразование) ко всем векторным элементам (не плиткам), поэтому проекция проецирует все в квадратную область размером в один пиксель и никогда не изменяется сzoom.

Чтобы решить эту проблему, поместите ваши круги так же, как в примере, размещает (и размеры) полигонов:

vector
  .attr("d", path(topojson.mesh(us, us.objects.counties)));

circle
  .attr("cx", projection(coord)[0])
  .attr("cy", projection(coord)[1])
  .attr("r", 5/(1<<12));

Обе эти позиции имеют одинаковый способ: только с проекцией, проецируя на квадрат в один пиксель.Увеличение применяет преобразование, чтобы покрыть весь SVG. Кроме того, поскольку мы масштабируем один пиксель, чтобы он соответствовал svg, мы хотим, чтобы радиус тоже масштабировался соответствующим образом .

Теперь мы можем применить преобразование к окружностям, аналогичным полигонам.:

circle
  .attr("transform", transform);

Конечно, мы могли бы уменьшать радиус и при каждом увеличении, используя увеличение k для изменения размера круга:

var pi = Math.PI,
    tau = 2 * pi;

var width = Math.max(960, window.innerWidth),
    height = Math.max(500, window.innerHeight);

// Initialize the projection to fit the world in a 1×1 square centered at the origin.
var projection = d3.geoMercator()
    .scale(1 / tau)
    .translate([0, 0]);

var path = d3.geoPath()
    .projection(projection);

var tile = d3.tile()
    .size([width, height]);

var zoom = d3.zoom()
    .scaleExtent([1 << 11, 1 << 14])
    .on("zoom", zoomed);

var svg = d3.select("svg")
    .attr("width", width)
    .attr("height", height);

var raster = svg.append("g");

var vector = svg.append("circle");

  // Compute the projected initial center.
  var center = projection([-98.5, 39.5]);

  // Apply a zoom transform equivalent to projection.{scale,translate,center}.
  svg
      .call(zoom)
      .call(zoom.transform, d3.zoomIdentity
          .translate(width / 2, height / 2)
          .scale(1 << 12)
          .translate(-center[0], -center[1]));

  var coord = [-100,40]
		  
  vector
      .attr("cx", projection(coord)[0])
	  .attr("cy", projection(coord)[1])
	  .attr("r", 5/(1<<12));

function zoomed() {
  var transform = d3.event.transform;

  var tiles = tile
      .scale(transform.k)
      .translate([transform.x, transform.y])
      ();

  vector
      .attr("transform", transform)
      .attr("r", 5/transform.k);

  var image = raster
      .attr("transform", stringify(tiles.scale, tiles.translate))
    .selectAll("image")
    .data(tiles, function(d) { return d; });

  image.exit().remove();

  image.enter().append("image")
      .attr("xlink:href", function(d) { return "http://" + "abc"[d[1] % 3] + ".tile.openstreetmap.org/" + d[2] + "/" + d[0] + "/" + d[1] + ".png"; })
      .attr("x", function(d) { return d[0] * 256; })
      .attr("y", function(d) { return d[1] * 256; })
      .attr("width", 256)
      .attr("height", 256);
}

function stringify(scale, translate) {
  var k = scale / 256, r = scale % 1 ? Number : Math.round;
  return "translate(" + r(translate[0] * scale) + "," + r(translate[1] * scale) + ") scale(" + k + ")";
}
<svg></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://d3js.org/d3-tile.v0.0.min.js"></script>
<script src="https://d3js.org/topojson.v1.min.js"></script>

В конечном итоге d3-плитка может показаться немного запутанной, поскольку вы используете довольно много систем координат (мозаика, масштабирование, пиксель, проекция, географическая).и, как правило, не проецирует всю карту в квадрат 1 пикселя.

...