OpenLayers latitude inaccurately captured in Webkit Mobile browsers - javascript

I am programming a page with a Map where I need to capture the location of the Tap/Click on a map and store the coordinates. I am using OpenLayers js. On desktop browsers (IE/FF/Chrome), this is working fine. On mobile devices, the tap is getting captured correctly on the default Android browser (both in real devices and emulators).
However on mobile webkit browsers (iPhone Safari & Android Chrome Beta), we are having a problem where the tap is getting captured for a few pixels higher (towards the north) of the actual tap. The error is not fixed (so, I can't just add 100 to the event's xy to recalibrate the top.)
Here is the code I am using as the clickhandler:
OpenLayers.Control.ClickHandler = OpenLayers.Class(OpenLayers.Control, {
defaultHandlerOptions: {
'single': true,
'double': false,
'pixelTolerance': 0,
'stopSingle': false,
'stopDouble': false
},
initialize: function(options) {
this.handlerOptions = OpenLayers.Util.extend(
{}, this.defaultHandlerOptions
);
OpenLayers.Control.prototype.initialize.apply(
this, arguments
);
this.handler = new OpenLayers.Handler.Click(
this, {
'click': this.trigger
}, this.handlerOptions
);
},
trigger: function(e) {
that.inputLonLat_EPSG4326 = null;
var lonlat = that.inputMap.getLonLatFromViewPortPx(e.xy);
that.logMessage("XY " + e.xy);
that.logMessage("LonLoat " + lonlat);
that.inputMarkers.clearMarkers();
that.inputMarkers.addMarker(new OpenLayers.Marker(lonlat,that.icons["green"].clone()));
lonlat.transform(that.inputMap.getProjectionObject(), new OpenLayers.Projection("EPSG:4326"));
that.inputLonLat_EPSG4326 = lonlat;
// For the moderation sections
$('#alertLatitude').val(that.inputLonLat_EPSG4326.lat);
$('#alertLongitude').val(that.inputLonLat_EPSG4326.lon);
//lonLat2.transform(that.inputMap.getProjectionObject(), new OpenLayers.Projection("EPSG:4326"));
that.logMessage("Stored lat " + that.inputLonLat_EPSG4326.lat);
that.logMessage("Stored lon " + that.inputLonLat_EPSG4326.lon);
that.callFunction(that.inputMapListener, e);
}
});
Should I be doing anything differently? Has anybody else seen the inaccuracy problem on mobile webkit browsers while using OpenLayers?

I finally found the reason this is happening. It seems that on webkit mobile browsers, the x,y that the library seems to be getting (or deriving) is based on the page and not on the element in which the map is housed. Hence the calculations are off. It seems that the inaccuracy can't be solved by adding the xy of the map element or some such DOM-based figure too (I tried it.)
I solved it by housing the Map in an IFrame and then have the iFrame housed within the map element. This way the x,y as received by the map click handler is accurate within the iFrame and hence the lat, long is also accurate. Since both the parent and iFrame are from the same domain, there are no issues communicating back and forth.
To complete the context, the iFrame based solution is definitely compatible with BB 9 & above, Android Chrome, Android Default and iPhone Safari, as tested by me.
Check out the solution at - http://suat1.vocanic.net//saralee/apps/dengue_alert/ and the iFrame at http://suat1.vocanic.net//saralee/apps/dengue_alert/mobileMap.php (WIP versions liable to change or break with time)

Related

Checking canvas dimension limitations

Discovering the Web Audio Api I wanted to draw a waveform for sound files. Now, I am aware of the image dimension limitations in some browsers, and I tried to look them up, but they seem to be ever changing (or at least memory differences like Chrome Desktop vs Chrome Mobile).
I tried to look up how to test if an image, or a Canvas / 2D Context can be of a certain size with almost no success. However, when testing this thing in Firefox I did get an error in the console so I tried the following method:
wfWidth = source.buffer.duration*100;
document.getElementById("waveform").width = wfWidth;
wfCtx = document.getElementById("waveform").getContext("2d");
var success = false;
while(success == false)
{
try {
var temp = wfCtx.getImageData(0,0,10,10); // this produced an error further down the code, so I use this as my test case.
success = true;
} catch(err) {
wfWidth = wfWidth / 2;
document.getElementById("waveform").width = wfWidth;
wfCtx = document.getElementById("waveform").getContext("2d");
}
console.log(success);
}
This does seem to work in Firefox as the console first outputs false showing that the canvas is too big and then true after halving the width of the canvas and the waveform is shown.
However, in Google Chrome on Desktop the canvas seems to be of a certain size (as indicated by a scroll bar) but it is totally blank. When I right-click to save image, it is a 0 byte txt file. On Chrome Mobile (android) I get this little square sad face. Guess that method of checking doesn't work in Chrome.
What would be the best way to test if the canvas is, well, valid, and resize if it is not?

Cordova 5.2.x Geolocation not Accurate

I have the following code in my app:
var geo = { lat: 0, lon: 0 };
navigator.geolocation.getCurrentPosition(
function( position ) {
// set global vars
geo.lat = position.coords.latitude;
geo.lon = position.coords.longitude;
},
function( error ) {
// handle error
geolocationError( error );
},
{ maximumAge: 3000, timeout: 5000, enableHighAccuracy: true }
);
For one reason or another the location that I get in my app using the above code is very different (100's of meters, sometimes a 1KM or more) than what is displayed in the native map application (i.e. iOS Maps). Why is this?
It's particularly a problem in places where there is no Wifi and I am indoors. For example, recently in an airport my position in the native map application was very accurate; I was positioned in the right terminal - but when I opened my app and refreshed it many times over several minutes the position wasn't anywhere near the terminal I was in, in fact I was about 1KM from the airport.
What do I need to do to get at the very least, the geolocation that the native apps are able to get?
I ran into this issue multiple times before - there isn't anything you can really do about it even if you set enableHighAccuracy to true. It's an issue with the HTML5 geolocation method. Turning on wifi and bluetooth increases the accuracy, but you can't guarantee a user will do that every time.
Best of luck.

Why is a Safari page breaking iOS rendering?

I know the title is not that explanatory but here is the story: I am developing a browser game, mostly using JavaScript and the Mapbox library.
Everything works well on desktop, Android and iOS but one problem appears on iOS: after letting the game run for a few minutes the phone suddenly begins to have graphic artifacts and display most of the text scrambled.
Here are some photos of what the phone begins too look like:
My question is: what exactly in my code can cause this? A memory leak? (LE: it turned out to actually be a memory leak)
The real question is: How comes that you can almost brick the entire phone by simply browsing a web page? Shouldn't Safari stop this, or at least the iOS ?
This is not a problem with this specific device, as this problem can be reproduced on different iPhone devices. (I'm not so sure about different iOS versions).
How I can reproduce the error:
Open the game (inside Safari).
Let it run for 3-4 minutes.
Slide down the notification center and everything goes crazy.
I have added a YouTube video showing how I can reproduce the error (on my iPhone 5C).
It seems that the issue first appears in the notification center (if you swipe down the menu from the top).
As for now, this problem seems to only occur on iPhone 5C iOS 9.2.1 (13D15). It also occurs on the new iOS 9.3 version.
In order to fix this issue I have to:
Close the Safari application (in which the game tab is open).
Lock the phone. After unlocking it everything is back to normal.
Some details about the game itself:
The game shows a Mapbox map and some units over it (markers).
A Node.js server runs at 1 tick/second and after each tick the updated game state is sent to the browser through Socket.io.
Every time the browser receives the game state it updates the markers accordingly.
*The game might also update markers if you zoom in or out or if you select them.
EDIT2:
Found the memory leak (as expected). After fixing this leak (check for undefined _icon) the issue no longer occurs. This means, that somewhere along those lines the Safari/iOS bug is triggered.
Here is what exactly was being called each tick, for each unit that was clustered (was hidden and grouped with others inside a MarkerCluster):
var $icon = $(marker._icon); // marker._icon is undefined because of the clustering
$icon.html('');
$icon.append($('<img class="markerIcon" src="' + options.iconUrl + '" />'));
var iconX = 10;
var iconY = -10;
var iconOffset = 0;
for(var v in this.icons) {
this.icons[v].css('z-index', + $icon.css('z-index') + 1);
this.icons[v].css('transform', 'translate3d(' + iconX + 'px,'
+ (iconY + iconOffset) + 'px,' + '0px)');
iconOffset += 20;
this.icons[v].appendTo($icon);
}
// Fire rate icons
this.attackRateCircle = $('<div class="circle"></div>');
this.attackRateCircle.circleProgress({
value: 0,
size: 16,
fill: { color: "#b5deff" },
emptyFill: 'rgba(0, 0, 0, 0.5)',
startAngle: -Math.PI / 2,
thickness: 4,
animation: false,
});
this.attackRateCircle.hide();
// Create and display the healthbar
this.healthBar = $('<div>').addClass('healthBar ');
this.healthBar.css('z-index', $icon.css('z-index'));
this.healthBarFill = $('<span class="fill">');
this.healthBar.append(this.healthBarFill);
$icon.append(this.healthBar);
$icon.append(this.attackRateCircle);
And this is the icons array:
this.icons = {
attack_order: $('<img src="img/attack.png" class="status_icon">'),
attack: $('<img src="img/damage.png" class="status_icon icon_damage">'),
hit: $('<img src="img/hit.png" class="status_icon icon_hit">'),
};
circleProgress call is from this library: https://github.com/kottenator/jquery-circle-progress
DEMO
Yay, I have been able to create a jsFiddle that reproduces the bug: https://jsfiddle.net/cte55cz7/14/
Open on Safari on iPhone 5C and wait a couple of minutes. On iPhone 6 and iPad mini the page crashes (as expected due to the memory leak)
Here's the same code in a HasteBin, for anyone who doesn't want to run it.
This memory leaks is probably due to how 'WebKit’s JS Engine' works [safari webkit-javascript llvm]
and really looks like to be a virtual memory buffer-overflow, having a direct impact on the remaining RAM (shared and used also by iOS to store User Interface graphical elements)
Relatively to the piece of code:
"[...]finding jQuery memory leaks is easy. Check the size of $.cache. If it’s too large, inspect it and see which entries stay and why. [...]" (http://javascript.info/tutorial/memory-leaks)
Let me expect that it is relative to this for loop :
for(var v in this.icons) {
this.icons[v].css('z-index', + $icon.css('z-index') + 1);
this.icons[v].css('transform', 'translate3d(' + iconX + 'px,'
+ (iconY + iconOffset) + 'px,' + '0px)');
iconOffset += 20;
this.icons[v].appendTo($icon);
}
Assuming inspection is done, and also assuming the fact that you find the entries, you may want to clean the data manually with removeData()
or you may use first $elem.detach() and then put $(elem).remove() in setTimeout.

cytoscape.js "e.originalEvent.x" with different behavior on browsers

I'm trying to build a crossplatform tool with cytoscape.js, wich includes mobile devices, but I'm facing some problems when using the "tap" function to add a node:
cy.on('tap', function(e) {
if(e.cyTarget === cy) {
if($("input:radio[id=btnAddNode]")[0].checked) {
var idNum = cy.nodes().size(),
setID = idNum.toString(),
offset = $cy.offset(),
position = {
x: e.originalEvent.x - offset.left,
y: e.originalEvent.y - offset.top
};
console.log("e.originalEvent.x: " + e.originalEvent.x);
console.log("offset.left: " + offset.left);
console.log("e.originalEvent.y: " + e.originalEvent.y);
console.log("offset.top: " + offset.top);
cy.add([{
group: "nodes",
data: {
id: "n" + setID
},
renderedPosition: {
x: position.x,
y: position.y
},
}]);
} else {
//show node data
}
}
});
On Chrome for Windows 7 (v.32) and also on Safari it is working as expected.
On IE 9 it's inserting the node about 30px higher and 5px left than the point where we click (?!)
But on Firefox (v.26) and Safari for iOS it's not working, it put's the node on the top-left corner of the canvas. Firefox debug says that the "originalEvent.x" and "originalEvent.y" properties are undefined
"e.originalEvent.x: undefined"
"offset.left: 8"
"e.originalEvent.y: undefined"
"offset.top: 55.66667175292969"
So, is this property obsolete? If so, what should be the best way to improve this code?
Thanks in advance.
EDIT: Almost solved, noticed that using e.originalEvent.pageX and e.originalEvent.pageY instead works well over all desktop browsers and on Windows Phone 8, except on iOS :(
Though the events in Cytoscape.js are similar to jQuery events, they are not jQuery events. Properties like pageX are available through e.originalEvent, though they are not normalised to e. In the upcoming v2.1 (or if you make zip in the repo), events have been updated with e.cyPosition and e.cyRenderedPosition. These values respectively provide the model position and rendered (on screen, relative to cy.js instance) position.
Also note that I don't think your approach would work on iOS etc even using jQuery events, because touch events aren't normalised. e.cyPosition and e.cyRenderedPosition do support touch events, though.

Google Maps in IE11 gives "Unspecified error"

I'm getting a very strange error when using custom Google Maps in a website of one of our clients. The map has some markers on it, and when you open the marker you can see a dialog with the address of that location. When I close this dialog (obviously by clicking the cross) in IE11, I get an "Unspecified error". For some reason, this error is being thrown from the method "getBoundingClientRect()". No other browser has this issue (not even IE8).
I am using Google Maps API version 3.14.
Does anyone know what this could be? I'm not sure if it's necessary to place any code, but I'm willing to do that if that makes everything more clear.
You can use this fix for IE (place this code on top):
HTMLElement.prototype._getBoundingClientRect=HTMLElement.prototype.getBoundingClientRect;
HTMLElement.prototype.getBoundingClientRect = function() {
try {
return this._getBoundingClientRect();
} catch(e) {
return { top : this.offsetTop, left : this.offsetLeft };
}
}

Categories

Resources