I have a requirement to display the current area of a Polygon during a Modify interaction.
I know how to listen for the modifyend event and, when it fires, calculate the area and display it in an overlay, but looking at the documentation, the interaction doesn't fire any other event while the vertex of a polygon is being dragged. I tried using the handleDragEvent function in the options of the Pointer interaction (which is the superclass of the Modify interaction) like this:
this.modify = new Modify({
handleDragEvent: (ev)=>{
console.log('drag event', ev);
// compute the new area and display it in an overlay
return true;
},
features: this.select.getFeatures(),
});
While the above does fire, defining the handleDragEvent in this way has the effect of not allowing the modification of the Polygon at all (I experimented returning both true and false and the effect is the same).
I was able to achieve the same effect in the Draw interaction by calculating the area and displaying it in an overlay inside the geometryFunction but I don't know how to achieve the same in the Modify interaction.
Any ideas? Code that simply fires when the vertex of a polygon is moved and has access to the feature / geometry being modified is enough, I can take it from there.
Related
How could I implement such mouseover effect that whenever mouse is over the linechart it shows every lines Y-value in an tooltip on hovered X?
So, in the end by moving mouse over the chart it should always show a tooltip that is updated constantly with Y value based on changed X? Now it shows tooltip only on X-scales steps e.g. 2010,2011,2012,2013,2014...
I don't have time at this very moment to write a complete solution, but I can try to point you in the right direction in terms of the part you will need that is directly related to the Google Charts API.
There Is Not A Simple Solution
First off, I'd like to make it very clear that there is not, to my knowledge, a simply solution built into the Google Charts API for this. Anything you right for this will involve rendering your own tooltip element, positioning it to the mouse location, and filling the tooltip with data yourself.
A JavaScript Framework of your choice will probably help a lot. Most have plugins or modules to handle mouseover and mouse position detection, though I can't recommend any specifically because I haven't tried this.
Chart Layout Interface
What you need to get the data values belonging to the mouse location is the Chart Layout Interface. You can get this as follows:
// create a line chart and set up a variable to store your interface
// on outside the scope of your ready handler, so you can use the
// interface in your mouse event code.
var layoutInterface;
var chart = new google.visualization.LineChart(document.getElementById('container-id'));
// set up a handler for the chart's ready event
// the chart layout interface is not available until the chart has
// been drawn
var readyHandler = function(){
layoutInterface = chart.getChartLayoutInterface();
};
// register the event handler
google.visualization.events.addListener(chart, 'ready', readyHandler);
I learned this from the demo here.
You will be using the getHAxisValue(xCoordinate) and getVAxisValue(yCoordinate) methods on the layout interface to get the data values corresponding to the x and y coordinates of the chart. The coordinates are relative to the chart's container element. See the Line Chart Documentation for information on methods available on the layout interface.
Mouse Event Handling
The mouse handling part of this is beyond the scope of my knowledge, but I do know that it is possible. I think you need to register a mouse enter event handler on your chart's container element, which would then register a mouse move, and mouse exit on the same element. The mouse exit would set display:none on your tooltip element and de-register the mouse move handler. The mouse move handler would set the absolute position of your tooltip element to the mouse location, and set it's content to the values retrieved from the chart layout interface.
Good Luck!
I'm making a game where the bubbles (circles) appear on the screen and move upwards and I'm using HTML5 and JavaScript only which means no frameworks like kinetic and no jQuery at all.
I've come to a point where I want to add an event listener to the bubble itself, now I understand that because the bubble is an object on the canvas, it has no method 'addEventListener' so I can't directly add a click event to the bubble, I was wondering if anybody could help me with this problem I'm having? Here is a fiddle of what happens so far...
Bubbles
I have tried to add the event listener to the bubble as specified before by doing this...
bubbles[i].addEventListener('click', function);
I have also tried adding mouseevents such as
bubbles[i].onmouseenter = function () { console.log("blah") }
But now, I'm seriously at a loss
Thanks in advance!
I have updated your sample on jsfiddle demonstrating hit-testing. This sample uses an onmousemove event handler bound to the canvas and performs the actual hit-test when this event occurs. The hit-test in your case is "Is the mouse coordinate with in the bubble's circle?"
Summary of changes:
A "bubble" has a color property to demonstrate the effect of a hit test.
function hitTest(x, y) { ... } used to test whether the passed in coordinates are within the bubble.
function getMouse(e, canvas) ... used to transform the mouse coordinates to canvas relative coordinates. i.e, the mouse coordinate needs to be relative to the upper left hand corner of the canvas to be accurate.
I am trying to set up OpenLayers to not display the vector layer just before a zoom starts and make it reappear after a zoom ends. I have the zoom ends part already established like this:
map = new OpenLayers.Map('map_element', { eventListeners: { "zoomend": mapEvent}});
function mapEvent(event) {
if(event.type == "zoomend") {
hide_vector_layer();
}
}
But I don't see any kind of event listener for the start of a zoom in the documentation. There is a "movestart" which covers moving, panning, and zoom. Unfortunately, I can't use the "movestart" one, because I don't want the layer to disappear during a pan. You would think there would be a "zoomstart", as there is a "zoomend".
The reason I am trying to do this, is because I don't like how the vector layer zooms at a different rate when using Google Maps as a base layer. It looks wrong, looks like all the features are inaccurate, even though they land in the right place after the zoom is complete.
Any suggestions?
Here is a easy to add the 'BeforeZoom' event to the OpenLayers . Just add the code below to where you created your map object.
map.zoomToProxy = map.zoomTo;
map.zoomTo = function (zoom,xy){
//Your Before Zoom Actions
//If you want zoom to go through call
map.zoomToProxy(zoom,xy);
//else do nothing and map wont zoom
};
How this works:
For any kind of zooming activity, OpenLayers API ultimately calls the function called zoomTo. So before overriding it, we copy that function to a new function called 'zoomToProxy'. The we override it and add our conditional zoom logic. If we want the zoom to happen we just call new proxy function :)
For this purpose you should override moveTo and moveByPx methods of OpenLayers.Map for eliminate movestart event triggering for any actions except zooming.
I had the same problem that OP had, and I tried to solve it with drnextgis's solution. But unfortunately it didn't completely work:: the zoomChanged property in OpenLayers.Map.moveTo evaluates to true not only when the zoom level has changed, but also when the map has been resized.
My map was 100% of the user's browser window, so if they resized the window, the event would be triggered. This was undesirable for me, as I only wanted to trigger the event if the zoom level had actually changed. My solution was to create an new event, called "zoomstart", which I inserted at the top of OpenLayers.Map.moveTo. Here's the code:
var getZoom = this.getZoom();
if ( !!getZoom && !!zoom && this.isValidZoomLevel(zoom) && getZoom != zoom )
this.events.triggerEvent("zoomstart", zoom);
This code will pass the new zoom level to an event listener that is registered to zoomstart, and in my case I determine the map's restrictedExtent and do other stuff based upon the new zoom level.
Peace be with ye.
"movestart" handles "zoomstart". To detect if the zoomstart, try:
map.events.register("movestart",map, function(e) {
if(e.zoomChanged)
{
//zoom start code here
}
});
Solution of "Shaunak" is worked very well for me.
I want to restrict zooming below 11 so edited his code as
if (zoom > 11) {
map.zoomToProxy(zoom, xy);
}
Ok, here is my problem, I'll put a picture to illustrate it easier.
I need the user to draw some polygons, representing the coverage area.
The polygon needs to have fixed number of points (vertex) because it goes into a processing algorithm later, and it would be really slow if a polygon can contain a lot of points.
Anyway, in my example lets stick to hexagons (6 points).
The user need to be able to drag the polygon around and modify it, but not change the number of points.
I tried setting the editable: true option for the polygon, it works fine, but it gives me the situation shown on the picture. It creates a handle for every point, and another handle (semi-transparent) in the middle between each points. Now, if the user moves that semi-transparent point, it will add another point (vertex) to the polygon, and add additional two handles in the middle of newly created lines. That gives us a 7 point polygon.
The best option would be to remove those semi-transparent handles, so the user can only drag polygon points, and it that way he can't affect the total number of points.
Can I achieve this using google maps editable option?
Another way to achieve what you want is to forego the built-in edit-ability of the polygon and implement it yourself. This is more powerful and flexible.
First, don't make the polygon editable. Next, make a Marker for each corner of the polygon. Finally, make each marker draggable and an event listener on it's "drag" event to update the polygon.
Making the markers and adding the event listener:
for (var i=0; i<coordinates.length; i++){
marker_options.position = coordinates[i];
var point = new google.maps.Marker(marker_options);
google.maps.event.addListener(point, "drag", update_polygon_closure(polygon, i));
}
Where update_polygon_closure is defined as:
function update_polygon_closure(polygon, i){
return function(event){
polygon.getPath().setAt(i, event.latLng);
}
}
Full code is in a jsfiddle here: https://jsfiddle.net/3L140cg3/16/
Since no one seems to have a better solution, I'm marking my workaround as accepted, in case someone stumbles upon the same problem. Not very pretty, but gets the job done
The only solution I found so far is to hide the handles manually after the polygon has been drawn. The problem here is that the handles don't have any CSS class or id, so I have to hide all divs with opacity 0.5 (opacity of the handles). It works, but it is pretty risky, considering that something else might have the same opacity and doesn't need to be hidden.
// variables
var map, path, color;
polygon = new google.maps.Polygon({
paths: path,
strokeColor: color,
strokeOpacity: 0.8,
strokeWeight: 2,
fillColor: color,
fillOpacity: 0.10,
editable: true,
});
polygon.setMap(map);
setTimeout(function(){ map.find('div[style*=opacity: 0.5]').hide(); }, 50);
As a slight improvement to #zolakt's answer, you can both hide the midpoint divs on the polygon and add a mousedown event listener to track when a midpoint is clicked to prevent dragging and changing the polygon:
// hide the midpoints (note that users can still click where the hidden midpoint
// divs are and drag to edit the polygon
$('#multi_markers div[style*="opacity: 0.5"]').hide();
// get the paths for the current polygon
var octopusPaths = HotelLib.octopusPolygon.getPaths();
// track when a polygon midpoint is clicked on
google.maps.event.addListener(HotelLib.octopusPolygon, 'mousedown', function(mdEvent) {
// if a midpoint is clicked on, mdEvent.edge will have an integer value
if(mdEvent.edge || (mdEvent.edge == 0)){
// immediately reset the polygon to its former paths
// effectively disabling the drag to edit functionality
HotelLib.octopusPolygon.setPaths(octopusPaths);
// hide the midpoints again since re-setting the polygon paths
// will show the midpoints
$('#multi_markers div[style*="opacity: 0.5"]').hide();
}
});
I just created an alternative solution to this without having to fiddle around with setTimeout or the polyline creation. This is also a somewhat global solution, so you can basically drop it in any established program that uses Google Maps.
We'll use MutationObserver to observe when those midpoint nodes appear on the DOM and then instantly hide them. They should start appearing when something is set as editable.
Basically just put this anywhere after the map is initialized:
var editMidpointNodeObserver = new MutationObserver(function(list, observer)
{
if($('#mapwrapper div[style*="opacity: 0.5"]').parent('div[style*="cursor: pointer"]').length > 0)
{
$('#mapwrapper div[style*="opacity: 0.5"]').parent('div[style*="cursor: pointer"]').remove();
}
});
editMidpointNodeObserver.observe($('#mapwrapper')[0], { childList: true, subtree: true });
Change the #mapwrapper to whatever the id of your Google Maps wrapper element is. I am using jQuery here, so therefore the $('#mapwrapper')[0] to convert jQuery object to a native DOM object. Should work without jQuery as well, I am assuming you know how to convert this to vanilla js.
We also just straight up remove the nodes, so no need to worry about user being able to click invisible ones by accident or otherwise.
MutationObserver should be supported in all browsers: https://caniuse.com/mutationobserver
I would like to find the current side and update the $currentFace property in my Cube class, every time the cube is rotated.
Here is my JSFiddle: http://jsfiddle.net/joecritch/tZBDW/
As you can see, I am updating the $currentFace successfully when I directly go to a face, using the menu. However, I am using a different approach for keyboard controls (using a 3D Matrix so that the cube doesn't go onto an incorrect axis).
What's the best way to calculate this?
I have an event handler for afterRotation, so the calculation code can be placed in there.
__afterRotation: function(e) {
// ...
// Insert calculation code?
// ...
}