Как обновить проекцию d3, чтобы она соответствовала преобразованию масштабирования?

Это мой обработчик масштабирования для моей карты:

const zoom = d3.zoom()
    .scaleExtent([1,25])
    .translateExtent([[width * -0.5, height * -0.5], [width * 1.5,height*1.5]])
    .on('zoom', (ev) => {
       svg.selectAll('path').attr('transform', ev.transform);  
    })

Он обновляет пути в svg, используя параметры преобразования из события. Это отлично работает, но если я использую projection(point) или подобные методы для возврата координат точки x,y, они будут неверными.

Я понимаю, что мне нужно обновить свою проекцию, чтобы обновить поведение масштабирования/панорамирования.

Если я запишу исходный перевод карты перед масштабированием const origTrans = projection.translate();, а затем применю преобразования x,y, то смогу правильно синхронизировать проекцию для верхнего уровня масштабирования (т. е. k=1).

.on("end", (ev)=> {
  projection.translate([origTrans[0] + ev.transform.x * ev.transform.k, origTrans[1] + ev.transform.y * ev.transform.k]);
  const c =  projection([-3.3632, 55]);
  svg.append("circle")
    .attr("cx", c[0])
    .attr("cy", c[1])
    .attr("r", 9)
    .attr("fill", "red");
  }); 

Мне неясно, как уровень масштабирования связан с масштабом проекции. Я не могу добиться того же

Я попробовал несколько вещей, например. - projection.scale(ev.transform.k) или projection.scale(projection.scale() * ev.transform.k) - Я предполагаю, что это еще не все? Если это поможет, я использую geoMercator для проекции.

Размещено ли на карте что-либо (что также масштабируется/панорамируется), что изначально не помещается в проекцию? Если да, то что размещается без привязки к проекции? Так или иначе, ответы есть, но будет проще, если все будет привязано к географическому положению.

Andrew Reid 06.05.2024 06:17

Да, все размещено с помощью проекции.

digitalWestie 06.05.2024 12:18
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
5
2
100
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Перечитывая свой вопрос поближе, вы можете усложнить проблему. Масштаб и сдвиг проекции могут быть полностью независимыми от состояния масштабирования SVG.

Связывание одного с другим создает больше проблем, чем оно того стоит, отчасти потому, что у вас увеличивается количество динамических систем координат, отчасти потому, что вам может потребоваться постоянно выполнять такие действия, как пересчет проецируемых точек во время событий перетаскивания (в зависимости от подхода, который может быть запаздывающим).


Насколько я понимаю проблему, ваши пути SVG масштабируются, но вам нужно извлекать, взаимодействовать, обновлять или отображать определенные точки и/или элементы, не являющиеся путями, в SVG, чтобы отразить их новое местоположение.

Почему бы не использовать тот же подход для кругов/точек/других элементов, что и для путей? Для этого я бы создал новый g для хранения всех масштабируемых элементов, путей и прочего, применил к нему преобразование масштабирования, таким образом масштабирование само позаботится обо всем масштабировании за вас:

let zoomG = svg.append('g');

zoom.on('zoom', (ev) => {
   zoomG.attr('transform', ev.transform);  
 })

Любые координаты дочерних элементов внутри ZoomG будут представлены с использованием значений пикселей проекции. Затем ZoomG преобразуется в целом в соответствии с масштабом.

Например, ниже показаны некоторые пути и начальный круг (Лондон). Независимо от состояния масштабирования Сингапур будет отображаться правильно при щелчке в любом месте карты (он исчезнет через несколько секунд, пока не будет выполнено повторное нажатие), в то время как существующие объекты будут правильно панорамированы и масштабированы.

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

var projection = d3.geoMercator()
    .scale(500 / 2 /Math.PI )
    .translate([250,250])
    
let zoomG = svg.append('g');

let zoom = d3.zoom()
  .on("zoom", (ev)=> zoomG.attr('transform', ev.transform))

  
svg.call(zoom);
  
svg.on("click", function(ev) {
    let xy = d3.pointer(ev, zoomG.node());
    let longlat = projection.invert(xy);
    console.info("mouse click at: " + xy + " which represents: " + longlat);
    
   zoomG.append("circle")
     .attr("cx", projection([103.820,1.352])[0])
     .attr("cy", projection([103.820,1.352])[1])
     .attr("r", 4)
     .transition()
     .attr("r", 0)
     .duration(2000)
     .remove();
    
})
  

d3.json("https://raw.githubusercontent.com/holtzy/D3-graph-gallery/master/DATA/world.geojson").then( function(data){
    
   zoomG.selectAll("path")
        .data(data.features)
        .enter().append("path")
            .attr("fill", "#eee")
            .attr("d", d3.geoPath()
               .projection(projection)
            )
            .style("stroke", "#ccc")
            
   zoomG.append("circle")
     .attr("cx", projection([0.128,51.507])[0])
     .attr("cy", projection([0.128,51.507])[1])
     .attr("r", 4)
     
})
<script src = "https://cdnjs.cloudflare.com/ajax/libs/d3/7.8.5/d3.min.js"></script>
<svg></svg>

Выше я также добавил демонстрацию того, как вычислить географическое положение мыши.

let xy = d3.pointer(ev, zoomG.node());
let longlat = projection.invert(xy);
console.info("mouse click at: " + xy + " which represents: " + longlat);

Нам не нужно беспокоиться об этом при переходе от проекционных координат к пиксельным координатам, поскольку вложение путей/кругов/что угодно в родительский G с преобразованием масштабирования позаботится об этом за нас. Но, двигаясь в обратном направлении, нам нужно рассмотреть преобразование масштабирования в том месте, где мышь фактически находится в проецируемом координатном пространстве (именно здесь на помощь приходит d3.pointer).

Да, я думаю, это имеет смысл. Пример с мышью помогает загрузить. Я должен был упомянуть контекст того, что я пытаюсь сделать, то есть - определить границу широты и долготы области просмотра после масштабирования/панорамирования. Хороший бонус в том, что более элегантно просто применить преобразование к одной группе, а не чем все пути!

digitalWestie 07.05.2024 15:32

Другие вопросы по теме