How To Get X,y Coordinates From Path Element Inside Svg?
Solution 1:
To answer the actual question:
The plugin you are using is controlling the transforms by changing the matrix of g.svg-pan-zoom_viewport
and not the viewBox
of the svg
.
In your function cursorPoint()
you keep transforming the mouse-coordinates referring to the svg
, yet you discard the underlying transformation on g.svg-pan-zoom_viewport
. That is the reason why you are getting different coordinates while the svg
is either resized or moved (=transformed).
If you refer the coordinates to the g.svg-pan-zoom_viewport
instead, you will get consistent results.
functioncursorPoint(evt){
pt.x = evt.clientX; pt.y = evt.clientY;
//return pt.matrixTransform(svg.getScreenCTM().inverse());var tGroup = document.querySelector('.svg-pan-zoom_viewport');
return pt.matrixTransform(tGroup.getScreenCTM().inverse());
}
Another way would be to change the viewBox
of the svg
instead of using the groups matrix. Yet since your plugin works that way, you should go with it.
Update
I played around with that linked plugin a bit and for me the function zoomAtPoint()
is doing something wrong. Let us assume Spain in the linked fiddle is at 165:165. Now to constantly zoom to that location correctly you need to reset it before:
panZoom.reset();
panZoom.zoomAtPoint(6, {x: 165, y: 165});
Else the function either does nothing or zooms somewhere else.
Now to get the coordinates of "argentinia" and zoom to it:
panZoom.reset();
//REM: Apparently the values need some time to adjust after reset() is called, yet is seems to have no callback.window.setTimeout(function(){
var tViewport = document.querySelector('g.svg-pan-zoom_viewport');
var tMatrix = tViewport.transform.baseVal.getItem(0).matrix;
var tBBox = document.querySelector('#argentina').getBBox();
var tPoint = {x: (tBBox.x + tBBox.width / 2) * tMatrix.a + tMatrix.e, y: (tBBox.y + tBBox.height / 2) * tMatrix.d + tMatrix.f}
//REM: Approximate values, I leave the exact calculation up to you.
panZoom.zoomAtPoint(6, tPoint);
}, 500)
Working fiddle with example buttons: https://jsfiddle.net/04Lg9ruj/
Solution 2:
What about a small change to how you attach the event listener?
Attach to each country instead?
$(document).ready(function() {
var panZoom = svgPanZoom('#mapa-mundi', {
zoomEnabled: true,
controlIconsEnabled: true,
fit: true,
center: true,
minZoom: 1,
maxZoom: 200,
zoomScaleSensitivity: 1
});
$(window).resize(function(){
panZoom.resize();
panZoom.fit();
panZoom.center();
})
var svg_rect = document.querySelector('#mapa-mundi').getBoundingClientRect();
alert("svg: " + svg_rect.top + " " + svg_rect.right + " " + svg_rect.bottom + " " + svg_rect.left);
$("#spain").on("click", function() {
panZoom.zoomAtPoint(12, {x: 188, y: 185});
});
//Find your root SVG elementvar svg = document.querySelector('#mapa-mundi');
//Create an SVGPoint for future mathvar pt = svg.createSVGPoint();
//Get point in global SVG spacefunctioncursorPoint(evt){
pt.x = evt.clientX; pt.y = evt.clientY;
return pt.matrixTransform(svg.getScreenCTM().inverse());
}
var country = document.querySelectorAll('.map-hover-svg');
var my_dict = {};
country.forEach(function(element){
var rect = element.getBoundingClientRect();
my_dict[element.id] = [rect.top, rect.right, rect.bottom, rect.left, rect.bottom - rect.top, rect.right - rect.left];
//console.log(element.id);//console.log(rect.top, rect.right, rect.bottom, rect.left);
});
country.forEach(function(element){
element.addEventListener('click',function(evt){
var loc = cursorPoint(evt);
var rect = element.getBoundingClientRect();
var curr_pan = panZoom.getPan();
var curr_zoom = panZoom.getZoom();
var curr_sizes =panZoom.getSizes();
var real_zoom = curr_sizes.realZoom;
alert(curr_pan.x + " " + curr_pan.y + " " + curr_zoom + " " + real_zoom);
panZoom.reset();
var my_x = my_dict[evt.target.id][3] - svg_rect.left + (my_dict[evt.target.id][5] / 2);
var my_y = my_dict[evt.target.id][0] - svg_rect.top + (my_dict[evt.target.id][4] / 2);
//panZoom.zoomAtPoint(3, {x: loc.x - curr_pan.x - svg_rect.left, y: loc.y - curr_pan.y - svg_rect.top});
panZoom.zoomAtPoint(3, {x: my_x, y: my_y});
alert(evt.target.id + " at " + loc.x +" "+ loc.y);
},false);
});
///svg.addEventListener('click',function(evt){///var loc = cursorPoint(evt);///alert(loc.x+" "+loc.y);///},false);
});
This way the event is only fired when you click those red countries. Also, the coordinates of the click seem accurate to me, I played around with it and I got the values expected.
Tried to initially loop through all elements with the class '.map-hover-svg' and add their top right bottom left to a dictionary/hash-table with the key as the id, then you can reference these dictionary items using the event.target.id.
Then you can use the offset of the svg element's top and left properties and half the width and height of the country path elements to always zoom to the middle of the country clicked:
Solution 3:
Actually you're in luck, I developed an hugo shortcode to zoom on a text from the SVG passed as a url parameter, for that I have to search for the text, find its id, from there it's the same as your goal to zoom on that id, so find the bounding box and apply the zoom.
A live demo is here https://www.homesmartmesh.com/docs/microcontrollers/nrf52/thread_sensortag/?svg=nrf52-sensor-tag&text=VEML6030
The source code is embedding the svg-pan-zoom in an hugo short code and the code of interest starts from here https://github.com/WebSVG/hugo-book/blob/7a7d44ea0f7f91a51c15bcb8e8efd294ef29f42f/static/js/setup-svg-pan-zoom.js#L70
text_highlight_zoom(embed,svg,element)
and the magic happens here
let e_box = element.getBoundingClientRect();
let svg_box = svg.getBBox();
const x = (e_box.x + e_box.width/2) / svg_box.widthconst y = (e_box.y + e_box.height/2) / svg_box.height
then it's a simple call to svg_pz.zoomAtPoint(z,{x:x,y:y},true)
Let me know if this level of details helps you or don't hesitate to ask if you have more questions.
And as bonus, you get a smooth css transition for the zoom operation otherwise the user does not understand what happened, and you get an svg filter animation to highlight the target text.
Post a Comment for "How To Get X,y Coordinates From Path Element Inside Svg?"