I have multiple groups of svg elements in one viewport. I want users to click on one group which will hide the other groups and enlarge the selected group to fill the viewport.
So far I have:
var continents = $(".continents")
for (var i = 0; i < continents.length; i++) {
continents[i].addEventListener('click', function(){
$(".continents").css("display","none");
var currentContinent=this;
currentContinent.setAttribute("transform","scale(1.0)")
})
}
Where the groups are classed ".continents". But this does nothing.
Here is a jsfiddle
Is it possible to create a zoom effect or simply enlarge a selected group?
There are two issues with the code:
Not all the groups have the class .continents, so not all of the continents will hide when you do this:
`$(".continents").css("display","none");`
only Asia and Africa do have that class, so only those two will hide.
When you set the attribute transform here:
currentContinent.setAttribute("transform","scale(1.0)")
you are not only modifying the value of the scale(), but you are also overwriting/deleting the value of the translation.
How to fix these issues:
Add the class .continents to all the groups.
Update both the values of scale and translate for the continent that is clicked, and not only the scale. And this is the tricky part: those values may not be the same for all the continents. For example, for Asia, the target values will be: translate(-400,439) scale(0.032,-0.032), but those values will not work for the other continents. You need to play with different values to find the ones that will work for each particular group.
You can see it working on this JSFiddle (notice that only Asia will work, the other continents will be displayed outside of the picture until you adjust the translate/scale values).
To make things as generic as possible, you could store the new values in a data- attribute (e.g.: data-transform), and then update the value of the transform by using the value of that data- attribute.
You don't have class defined on all of your group elements so the click handler and css is only applied to 2 of the groups.
Also, you set all of the displays to none, and never set the display of the selected group back to inline.
The transform is no good since the paths are much larger, have an inverted y axes and are positioned absolutely, so changing the scale from 0.017, -0.017 to 1.0, 1.0 moves them far off the viewport.
JSFiddle
var prevTransform = null;
var continents = $("g");
for (var i = 0; i < continents.length; i++) {
continents[i].addEventListener('click', function () {
var currentContinent = this;
if (prevTransform == null) {
console.log("Click!");
$("g").css("display", "none");
prevTransform = currentContinent.getAttribute("transform");
currentContinent.setAttribute("transform", "translate(-20,220) scale(0.025, -0.025)");
$(currentContinent).css("display", "inline");
} else {
currentContinent.setAttribute("transform", prevTransform);
prevTransform = null;
$("g").css("display", "inline");
}
});
}
In this example, South America works best, the others move too far up and right. Australia moves out of view.
Related
I have a Leaflet Map on a Shiny web-app. I have 3 Base Groups containing different circles, and 1 Overlay Group containing polylines crossing each other. The polylines are sorted so that the more important lines are on top of the less important one. The circles are added afterwards to be on top of the lines.
The problem is : when unchecking then checking the overlay group, the lines are put on top of the circles. The circle can be put on top of the lines when the base layer is changed.
However, I would like to prevent the line from coming on top of the circles.
To do that I used the OnRender function of htmltools :
output$map <- renderLeaflet(
leaflet() %>%
addProviderTiles(providers$CartoDB.Positron) %>%
setView(
lng = 48.80,
lat = 2.30,
zoom = 10
) %>%
addControl("Title", position = "bottomleft", className = "map-title")
%>% onRender(
"function(el, x) {
this.on('overlayadd', function(e) {
e.layer.bringToBack();
})
}"
)
And, well, yeah it does put the line behind the circles, but what it also does is reversing the order of the lines (of the whole FeatureGroup), so now the less important lines are on top of the more important. From what I get, bringToBack() take the first drawn line, bring it to the back, then the second drawn line, put it to the back (under the first one) and so on, so the first drawn (the least important) will be put to the front while the last drawn (the most important) will be put to the back.
So I tried a few modification of the function :
function(el, x) {
this.on('overlayadd', function(e) {
var layers = e.layer.getLayers();
e.layer.clearLayers();
layers.reverse();
layers.forEach(l => e.layer.addLayer(l));
e.layer.bringToBack();
})
And it didn't work. So now I don't get it, because technically now the first drawn is the one that I want to be last drawn, and therefore, the reversing of the bringToBack should do it. But no, the order is still the same as the other function. And,I tried without the reverse, it is still the same result.
I also tried setZIndex, putting the base layer at a high Z-Index, while the Overlay is brought back to a low Z-Index, but it didn't had any effect (lines on top of the circles).
And I tried to change the autoZIndex and sortLayers options of layerControlOptions and it had no effect (lines on top of the circles).
So what I would like is : either an explanation of the magic behind bringToBack, or a js function that will do the trick, or an R solution that will keep the order how I want it.
The function addMapPane in library(leaflet) can be used to fix this problem without custom JS code.
From the description:
map panes can be created by supplying a name and a zIndex to control layer ordering. We recommend a zIndex value between 400 (the default overlay pane) and 500 (the default shadow pane). You can then use this pane to render overlays (points, lines, polygons) by setting the pane argument in leafletOptions. This will give you control over the order of the layers, e.g. points always on top of polygons. If two layers are provided to the same pane, overlay will be determined by order of adding. See examples below. See https://leafletjs.com/reference-1.3.4.html#map-pane for details.
See the example in the help. Type ?addMapPane in the console
I am using a c3 chart. I have a button to download it using canvg. I also have it toggle bars when legends are clicked. The download works and so do the toggles.
The only issue in the downloads is that I can either always show the legends (even if the bars they correspond to aren't being shown) or as soon as I toggle a legend the legend never appears again in the downloads (although it does on the chart itself).
What I would like is for the legends to only appear if their corresponding bars are actually being shown. I also don't want the legends to be hidden if their bar is being shown. (Legend Shown <=> Bar Shown kind of relationship)
I had issues with IE in the past so following https://github.com/c3js/c3/issues/2528 the display is 'Block'.
var string = ".c3-legend-item-hidden";//hides legends that are unselected in the download. Will KEEP them hidden even if retoggled :(
d3.selectAll(string).each(function() {
var element = this;
var computedStyle = getComputedStyle(element, null);
for (var i = 0; i < computedStyle.length; i++) {
var property = computedStyle.item(i);
element.style[property] = computedStyle.getPropertyValue(property);
}
});
//removing this section makes all legends appear permanently regardless of whether the bar does
Expected: a graph that has the correct bars and legends shown in the downloads
Actual:
(with code segment) hidden legends that do not reappear when needed
(without code segment) legends that are never hidden
Update: Just to clarify, this works when converting the graph to a downloaded svg file (adding xmlns etc.), just not when using canvg and downloading to a png file (which is what I need it to do).
Instead of using the computed style, manually set the styles you need.
This solution worked for me ('hidden' legends are slightly visible but it is now identical to my actual chart which is all I personally needed):
var string = ".c3-legend-item-hidden";
d3.selectAll(string).each(function() {
//this.style['visibility']='hidden'; // uncomment this out and it will completely hide the legend if that is what you want
this.style['opacity']= 0.15;
// set opacity as 0 to make the legend transparent to the point you can't see it (if that is what you want)
// or 0.15 to make it like the chart (as done here and in c3.css)
});
The computed styles give a lot more styles than you need and can override the styles you want.
I'm building a custom location selector for the user, which is essentially a map (image) and a text input where the user enters his country. The custom map is about 1500x1000px. The final goal is to slowly auto-scroll the map to a certain pre-defined x,y of the selected country. How can I define, lets say, that Germany is located at 700x800, and center the map image around that point when the user selects that country from the drop down list?
Here's my fiddle, which includes the move-able map (I'm not adding any code in here because it is long, please excuse me and use the fiddle to browse the code)
Use the Position object for this. Let's say
var germany = new Position(-700,-800);
You can set the position by using the rest of your code:
germany.Apply(document.getElementById('draggableElement'));
Although I guess that it will set the position of the top left corner and not the center, so you'd need to keep that in mind.
EDIT: Changed the numbers to negative numbers because this will actually move the picture up and left which I assume is the behaviour you want.
For presentation purposes I have added a button to your Fiddle. Although this is definitely not germany ;)
EDIT2:
To animate the position change with jQuery's animate just use this syntax:
$('#draggableElement').animate({
left: germany.X,
top: germany.Y
});
Also see the updated JSFiddle.
Please take a look at http://jsfiddle.net/6WNQM/1/ which combines normal imagemap HTML, CSS and jQuery 1.9.
The jquery code:
jQuery(document).ready(function ($) {
$('#regions_fr').mapster({
mapKey: 'name',
singleSelect: true,
fillOpacity: 0.6,
fillColor: 'FF0000',
onMouseover: function (evt) {
var parts = evt.key.split('-');
var part = parts[1];
highlightArea(part);
}
});
$('a').hover(function () {
var parts = $(this).closest('li').attr('class').split('-');
var part = parts[2];
highlightArea(part);
});
});
function highlightArea(key) {
$('area[name=part-' + key + ']').mouseenter();
$('a').removeClass('hover');
$('li.menu-item-' + key + ' a').addClass('hover');
}
You see the map of France which is divided in several (geographical) regions. Overlapping you see some tourist areas that are overlapping 1 or more regions and also overlapping each other.
I'm stuck right now. I want to see specific functionality, but after trying several plugins and CSS techniques, I'm wondering if it's even possible what I want:
First desired functionality is that the hover over a region highlights the corresponding textlink and vise versa. This works :)
Second is that the geographical region "Bourgogne" can be hovered, but in IE only the top part can be hovered and becomes highlighted. Any idea?
Then we have the overlapping part of the two touristregions'. I want them to work as a group when the overlapping part is hovered. In other words, when I click on the overlapping part I want to see the overlapping part being highlighted and the selectbox of "tour-regio 1" and "tour-regio 2" both being checked.
I'm very curious if someone can help me out here, I'm stuck. Thanks in advance.
I have a chart created with Birt and I want to add some label on it to specify the 4 regions delimited by the 2 red markers (see image below), one label in each quadrant (centered if possible).
I am looking for a solution to do that, directly using birt chart editor or by using a javascript (like I have done for the red markers).
In order to dynamically center some type of label in each quadrant of the graph, you'll have to have some way of calculating the coordinates. Of course, I'm not really familiar with Birt and I'm making the assumption that the graph's red markers will vary.
Anyway, assuming that you can get the coordinates, you could write a function that would dynamically generate the label based on a couple of parameters:
function generateLabel(sContent, iXoffset, iYoffset) {
var eLabel = document.createElement('span');
eLabel.appendChild(document.createTextNode(sContent));
var eDivision = document.createElement('div');
eDivision.appendChild(eLabel);
eDivision.style.left = iXoffset + 'px';
eDivision.style.top = iYoffset + 'px';
// include other styles here...
return eDivision;
}
and then from there, you can call this function with the set label content and offset coordinates:
var eQuadrantOneLabel = generateLabel('Quadrant One', 10, 25);
// and so on...
Then just add the element to the graph's container (assuming that is has an id of, say, graph):
var eGraphContainer = document.getElementById('graph');
eGraphContainer.appendChild(eQuadrantOneLabel);
// and so on for each label...