How to enable Draggable Markers for iPhone/iPad using the Nearmap API
We’re currently working on a solution for a client that makes use of Nearmap.com (under license) for up to date imagery of residential properties. Most users of the system will be accessing the site from iPads in the field while creating quotes for clients and the app involves placing and moving markers around the customer’s property image.
The problem is that in it’s current state, the Nearmap API does not support dragging markers when using maps from an iPad or iPhone. They do support moving the map around and pinch to zoom gestures, but not dragging.
To get around this issue we had to add some of our own event handlers to the Nearmap namespace. There were no specific functions for handling touch events on markers, but there are events for handling mouse clicks. So the process we went through was to duplicate the mouse events with touch events, and then make sure that they didn’t interfere with each other.
The trick is to make sure you’re registering the events at the right point and also making sure that you remove them once you’re done with the drag in touchend.
Here is the modifications we made in the hope that it might help someone else somewhere down the line (or at least a reminder to us!).
If you find something wrong or missing (which I’m almost sure there will be, the original file is 13,000 lines and I’m quickly trying to find what we did again) here let us know in the comments. But hopefully this will give you the general gist of what we did and help you along your way
Add new Marker functions:
Marker.prototype.onmarkertouchmove = function (e){
var map = this.map;
var dragged = this.moved;
var latlng;
var latlngPx;
var mapType;
var proj;
var tScale;
var heading;
var distMoved;
var sclDistMoved;
var rotatedDistMoved;
this.moved = true;
if (this.draggingEnabled()) {
if (e.touches.length == 1){
var touch = e.touches[0];
e = e || window.event;
mapType = map.getCurrentMapType();
proj = mapType.getProjection();
heading = mapType.getHeading();
tScale = mapType.getTileScale();
latlng = this.getLatLng();
latlngPx = proj.fromLatLngToPixel(latlng, map.getZoom(), mapType.getTileSize());
distMoved = new nmm.Point(touch.clientX - this.clientX, touch.clientY - this.clientY);
sclDistMoved = new nmm.Point(distMoved.x / tScale.x, distMoved.y / tScale.y);
rotatedDistMoved = nmlm.rotatePoint(heading, sclDistMoved, nmm.Point.ORIGIN, Math.round);
this.clientX = touch.clientX;
this.clientY = touch.clientY;
latlngPx.add(rotatedDistMoved.x, rotatedDistMoved.y);
this.setLatLng(proj.fromPixelToLatLng(latlngPx, map.getZoom(), false, mapType.getTileSize()));
Marker.prototype.moveIcons.call(this);
if (!dragged) {
this.foregroundImage.style.cursor = nmls.CURSOR_GRABBING.css;
map.setDraggingCursor(nmls.CURSOR_GRABBING.css);
Marker.prototype.renderPickup.call(this);
nmev.trigger(this, "dragstart", this.getLatLng())
}
nmev.trigger(this, "drag", this.getLatLng());
return nmev.preventDefault(e)
}
}
};
Marker.prototype.onwindowmarkertouchend = function (e){
if (this.opts.draggable && this.moved){
Marker.prototype.renderDrop.call(this);
this.map.setDraggableCursor(nmls.CURSOR_GRAB.css);
nmev.trigger(this, "dragend", this.getLatLng());
nmev.trigger(this, "mouseover", this.getLatLng());
nmev.trigger(this, "mouseout", this.getLatLng());
}
else{
if (!this.moved) {
Marker.prototype.onmarkertouchstart.call(this, e)
}
}
this.foregroundImage.style.cursor = "pointer";
nmev.removeListener(this.onwindowmarkertouchend);
nmev.removeListener(this.onmarkertouchmove);
delete this.onwindowmarkertouchend;
delete this.onmarkertouchmove;
delete this.latlngPx;
delete this.clientX;
delete this.clientY;
delete this.moved;
return nmev.preventDefault(e)
}
Marker.prototype.onmarkertouchstart = function (e) {
e = e || window.event;
if (e.touches.length == 1){
var touch = e.touches[0];
this.clientX = touch.clientX;
this.clientY = touch.clientY;
this.onmarkertouchmove = nmev.addDomListener(document, "touchmove", nmev.callback(this, Marker.prototype.onmarkertouchmove));
this.onwindowmarkertouchend = nmev.addDomListener(document, "touchend", nmev.callback(this, Marker.prototype.onwindowmarkertouchend));
nmev.trigger(this, "touchstart", this.getLatLng());
nmev.stopBubbling(e);
return nmev.preventDefault(e);
}
};
Marker.prototype.onmarkertouchend = function (e) {
nmev.trigger(this, "touchend", this.getLatLng());
nmev.trigger(this, "mouseup", this.getLatLng()) ;
nmev.trigger(this, "mouseover", this.getLatLng()) ;
nmev.trigger(this, "mouseout", this.getLatLng()); };
foregroundImage.ontouchstart = nmev.callback(this, Marker.prototype.onmarkertouchstart);
foregroundImage.ontouchmove = nmev.callback(this, Marker.prototype.onmarkertouchmove);
foregroundImage.ontouchend = nmev.callback(this, Marker.prototype.onmarkertouchend);
Add new DraggableObject functions:
DraggableObject.prototype.ontouchstart = function (e, dragObj) {
var touch;
if (dragObj._enabled && e.touches.length === 1) {
touch = e.touches[0];
dragObj.mouseX = touch.clientX;
dragObj.mouseY = touch.clientY;
dragObj.hasDragged = false;
dragObj._ontouchmove = nmev.addDomListener(document, "touchmove", nmev.callbackArgs(document, DraggableObject.prototype.ontouchmove, dragObj));
dragObj._ontouchend = nmev.addDomListener(document, "touchend", nmev.callbackArgs(document, DraggableObject.prototype.ontouchend, dragObj));
dragObj._onmousemove = nmev.addDomListener(document, "mousemove", nmev.callbackArgs(document, DraggableObject.prototype.onmousemove, dragObj));
dragObj._onmouseup = nmev.addDomListener(document, "mouseup", nmev.callbackArgs(document, DraggableObject.prototype.onmouseup, dragObj));
if (window.parent.location.href !== window.location.href) {
dragObj._onframetouchend = nmev.addDomListener(document, "touchend", nmev.callbackArgs(document, DraggableObject.prototype.onframemouseout, dragObj))
}
return nmev.preventDefault(e)
}
};
DraggableObject.prototype.ontouchmove = function (e, dragObj) {
var touch;
var x;
var y;
var style;
if (dragObj._enabled && e.touches.length === 1) {
touch = e.touches[0];
x = touch.clientX - dragObj.mouseX;
y = touch.clientY - dragObj.mouseY;
if (x !== 0 || y !== 0) {
if (!dragObj.hasDragged) {
dragObj.hasDragged = true;
nmev.trigger(dragObj, "dragstart", touch)
}
dragObj.moveBy(new nmm.Size(x, y));
dragObj.mouseX = touch.clientX;
dragObj.mouseY = touch.clientY;
nmev.trigger(dragObj, "drag", touch)
}
}
return nmev.preventDefault(e)
};
DraggableObject.prototype.ontouchend = function (e, dragObj) {
var hasDragged = dragObj.hasDragged;
var touch;
hasDragged = dragObj.hasDragged;
delete dragObj.mouseX;
delete dragObj.mouseY;
delete dragObj.hasDragged;
touch = e.touches[0];
dragObj.dragend(touch)
};
Modify existing DraggableObject functions:
function DraggableObject(src, opts) {
var style = src.style;
this._src = src;
this._enabled = true;
opts = nmo.Synchronize.fill(opts, DraggableObjectOptions);
if (opts.draggableCursor) {
this._draggableCursor = opts.draggableCursor
}
if (opts.draggingCursor) {
this._draggingCursor = opts.draggingCursor
}
if (opts.left) {
this._left = opts.left
}
else {
if (!style.left) {
this._left = 0
}
else {
this._left = parseInt(style.left, 10)
}
}
if (opts.top) {
this._top = opts.top
}
else {
if (!style.top) {
this._top = 0
}
else {
this._top = parseInt(style.top, 10)
}
}
style.cursor = this._draggingCursor;
style.cursor = this._draggableCursor;
if (style.position !== "relative" || style.position !== "absolute") {
style.position = "absolute"
}
style.left = this._left + "px";
style.top = this._top + "px";
this._onmousedown = nmev.addDomListener(src, "mousedown", nmev.callbackArgs(src, DraggableObject.prototype.onmousedown, this));
this._ontouchstart = nmev.addDomListener(src, "touchstart", nmev.callbackArgs(src, DraggableObject.prototype.ontouchstart, this));
}
DraggableObject.prototype.dragend = function (e) {
var hasDragged = this.hasDragged;
if (this._onmousemove)
{
nmev.removeListener(this._onmousemove);
}
if (this._onmouseup)
{
nmev.removeListener(this._onmouseup);
}
if (this._ontouchmove)
{
nmev.removeListener(this._ontouchmove);
}
if (this._ontouchend)
{
nmev.removeListener(this._ontouchend);
}
if (this._onframemouseout) {
nmev.removeListener(this._onframemouseout)
}
delete this._onmousemove;
delete this._onmouseup;
delete this._ontouchmove;
delete this._ontouchend;
delete this._onframemouseout;
delete this.mouseX;
delete this.mouseY;
delete this.hasDragged;
if (this._enabled) {
this._src.style.cursor = this._draggableCursor;
if (hasDragged) {
nmev.trigger(this, "dragend", e)
}
nmev.trigger(this, "mouseup", e);
nmev.trigger(this, "click", e);
}
};







