So I'm having a problem generating my jVectorMap.
The map itself sits inside a very custom drop down menu that I have created and this is where I suspect the problem is.
When I mouseover my menu item to open up the drop down which contains the map the actual svg starts out with a forced dimension of 100px x 100px.
What I have tried to do a number of workarounds wher I call the "map.setSize()" either on the mouseclick event of the dropdown as well as the mouseover event of the container itself. The problem here is my dropdown is not subject to a click event but shows on the mouseover event. However, at the point of the mouseover event the actual container for the map hasn't loaded so I'm still stuck with a 100px x 100px svg.
To get around this I've put an event on the mouseover event of the container itself but this isn't great either as it then requires the user to move his mouse over the container before it actually shows the map, something I don't want to happen.
Is there a way of getting the map built inside a div which is invisible before my menu event occurs?
For an example of my problem I've created this at jsfiddle http://jsfiddle.net/AEup9/
You will notice that when you hover over the "Show Map" menu item (the only item) the drop down is blank except for the topic headers until you move the mouse over the actual drop down itself which then reloads the map. I then keep the map there by using my "loaded" variable created before my mouseover event and a force map.setSize() inside the same event:
var loaded = false;
$('#aamap').mouseover(function () {
if (!loaded) {
(function () {
map = new jvm.WorldMap({
map: 'za_mill_en',
container: $('#southafrica-map'),
backgroundColor: '#cbd9f5',
initial: {
fill: 'white'
},
series: {
regions: [{
attribute: 'stroke'
}]
}
});
loaded = true;
})();
}
map.setSize();
});
This is my rough work around but not what I really want as I want the map to show up first time.
Can anyone help me here?
Edit: I finally decided to NOT go ahead with using jvectormap due to this issue. Instead I opted to use jqvmap which is to some degree a fork of jvectormap, however the issues experienced with jvectormap were no longer a problem.
I met this issue as well.
To solve that problem we need to run an updateSize method on a map object when container of our map becomes visible. First, to get the map object we need to use this command:
$('#world-map').vectorMap('get', 'mapObject') and when execute updateSize on it, like:
var map = $('#world-map').vectorMap('get', 'mapObject');
map.updateSize();
or in a shorter form:
$('#world-map').vectorMap('get', 'mapObject').updateSize();
Related
The closest to my question are: Mapbox gl js - overlapping layers and mouse event handling, Mapbox add popup on hover (layer), close on mouseleave, but keep open on popup hover and How to ignore mouse events on a mapbox layer - but they don't answer it.
I have two layers, let's imagine one is for a country, and another is for a city. Both of them a handled like this:
// CountryLayer.js
map.on("mousemove", "country-layer", e => {
// show info about the country
featureProps = e.features[0] // display some notification from the props
...
// CityLayer.js
map.on("mousemove", "city-layer", e => {
// show info about the city
featureProps = e.features[0] // display some notification from the props
...
It's done in different components. But when I mouseover city-layer mapbox thinks that I'm still "mousemoving" on top of the country-layer as well, so I get two notifications from separate components, where I need only one - in that case from the city-layer cause it's on top of country-layer.
Handling the mousemove without layerId in one place is gonna be a mess and breaks all the good rules about programming. Creating external "event manager" which will track whether I'm hovering the city and if is so will remove mousemove event from country-layer - is complex. I didn't find any good alternatives. At least, I would be glad to disable pointer events for a layer like this:
map.on("mousemove", "city-layer", e => {
map.getLayer("country-layer").setStyle({ "pointer-events": false })
featureProps = e.features[0]
...
or something like this. Is it possible? Is there more adequate way around it?
e.originalEvent.stopPropagation(); does not work
It appears, that you can do e.originalEvent.preventDefault(); in the city-layer and add a check e.originalEvent.defaultPrevented in the country-layer from the example.
However, I have issues with z-index position of layers: map.moveLayer("city-layer", "country-layer"); or vice-vise doesn't actually change the way, event propogates, so for some reason my country-layer always come first, so when it checks for e.originalEvent.defaultPrevented, preventDefault() wasn't fired yet, so it comes always false.
But technically, this answers my question.
map.on can listen on multiple layers at once. With events like mousemove it returns features in reverse layer order (from "highest" to "lowest").
So the way to do this is:
map.on("mousemove", ["city-layer", "country-layer"], e => {
feature = e.features[0]
if (feature.layer.id === 'city-layer') {
// ...
} else {
// ...
}
});
I need to be able to click on background, and get the position of the click.
I tried adding an event listener to stage like this
app.stage.interactive = true;
app.stage.on('click', function(){
console.log('hello');
})
but it works only if i click on element inside the stage, not the background itself.
Do i need to make a sprite as a background, if so, how do i set its background color and make sure it stays under all the other elements?
The stage is a PIXI.Container, which means it's basically an empty node that can hold children. It doesn't have dimensions of its own, and so when the interaction manager goes to hit-test your click, it isn't detected.
Your suggestion of adding a background sprite is probably the simplest solution. You can add one like so:
// Create the background sprite with a basic white texture
let bg = new PIXI.Sprite(PIXI.Texture.WHITE);
// Set it to fill the screen
bg.width = app.screen.width;
bg.height = app.screen.height;
// Tint it to whatever color you want, here red
bg.tint = 0xff0000;
// Add a click handler
bg.interactive = true;
bg.on('click', function(){
console.log('hello');
});
// Add it to the stage as the first object
app.stage.addChild(bg);
// Now add anything else you want on your stage
...
PixiJS renders objects in order, so if you add your background sprite as the first child of the app's stage, it will be rendered behind all other content. When hit-testing a click, it will be the last object tested, and so will catch a click on the background of the stage. Note that to "block" clicks, other objects will need to be set to interactive = true, even if they don't have a click handler attached!
A possible solution would be to add an event on app's view.
app.renderer.view.addEventListener('click', function(e) {
console.log(e);
});
I'm using leaflet js to build a map with some pins https://leafletjs.com/ and I'm also allowing drawing of shapes, e.g. polygons, circles etc. I also these to be edited using a plugin called leaflet.pm https://github.com/codeofsumit/leaflet.pm.
There are events here but none of the events are giving be back the cordinates of the new position after edit mode has been disabled or after the drag has finished. This is the event I have hooked into;
map.on('pm:globaleditmodetoggled', function(e) {
console.log(e);
});
Wheres this event gives me what is required;
map.on('pm:create', function(e) {
let obj = {
type: e.shape,
coordinates: e.layer.editing.latlngs[0][0]
};
$('#cords').val(JSON.stringify(obj))
});
Any ideas how when the shape is edited that I can get the update coordinates?
I'm Sumit, the maintainer of leaflet.pm
What you can do is: listen to an event being created and add the edit event to the new shapes:
map.on('pm:create',(e) {
e.layer.on('pm:edit', ({ layer }) => {
// layer has been edited
console.log(layer.toGeoJSON());
})
});
Of course, whenever you add a layer to the map you can also apply the pm:edit event to its reference.
Additionally, when you create layers or add layers to the map, you can simply store the reference. When editing is done you can just check the reference for it's coordinates (just as you would normally in leaflet). If you just need to know when editing is done, use the pm:edit event to catch whenever a layer was edited.
Hope this helps 👍
I am looking at the Data Maps library of D3, and I would like to know how to go about manipulating the svg content. Let's say I look at the basic world map, how would I run a alert("Selected") by clicking on Canada? Or change its background color by clicking on it?
EDIT: In my specific instance, I am using the US map. I use the following lines to boot up the map:
var map = new Datamap({element: document.getElementById('maincontains'),
scope: 'usa',
fills: {defaultFill: 'rgb(217, 217, 217)'}
});
Now, in the usa.js I find IDs such as "NY". However, this does not allow me to
document.getElementById("NY").addEventListener('click', function(){
alert("New York");}
The documentation on Events says the following:
All events are bubbled up to the root svg element and to listen to
events, use the done callback.
So in order to bind a click event and alert the name, use the done event of the map object and use the svg object inside the event handler:
<script>
var map = new Datamap({
element: document.getElementById('container'),
done: function(datamap) {
datamap.svg.selectAll('.datamaps-subunit').on('click', function(geography) {
alert(geography.properties.name);
});
}
});
</script>
My example click on Greenland:
Further restrictions (e.g. only specific countries) could be made using the properties.
As of right now, I have a graph for which I would like to calculate an average slope. I would like to do this by having the user select a left point and a right point on a line. I know how to record where a person clicked, but I would also like to display the same circular hover effect where they clicked until they click on it again to turn it off. Is there a special flot method that would allow me to do this, or will I have to manipulate the flot documentation (how would I go about doing that if I need to)?
You need to make the chart clickable (I guess you already have that) by setting the clickable option:
grid: {
clickable: true
}
Than bind an event handler to the plotclick event which calls the highlight method, when clicking on a data point:
$("#placeholder").bind("plotclick", function (event, pos, item) {
if (item) {
plot.highlight(item.series, item.datapoint);
}
});
(Snippets taken from the example page)