Это мой обработчик масштабирования для моей карты:
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 для проекции.
Да, все размещено с помощью проекции.



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Перечитывая свой вопрос поближе, вы можете усложнить проблему. Масштаб и сдвиг проекции могут быть полностью независимыми от состояния масштабирования 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).
Да, я думаю, это имеет смысл. Пример с мышью помогает загрузить. Я должен был упомянуть контекст того, что я пытаюсь сделать, то есть - определить границу широты и долготы области просмотра после масштабирования/панорамирования. Хороший бонус в том, что более элегантно просто применить преобразование к одной группе, а не чем все пути!
Размещено ли на карте что-либо (что также масштабируется/панорамируется), что изначально не помещается в проекцию? Если да, то что размещается без привязки к проекции? Так или иначе, ответы есть, но будет проще, если все будет привязано к географическому положению.