2010-11-21

Capturing right-click events in OpenLayers

I've been looking at using OpenLayers as the mapping API for an application I've been working on for some time.  One of the key features of the application requires capturing mouse click events on the map.  This is fairly normal with OpenLayers for many of its controls, but when the right-click event is involved, I would always get the default context menu from the browser, and OpenLayers didn't seem to capture the rightclick event on its own.  There are a few workarounds posted on the web...but I found they were not easily adaptable, or didn't quite work the way I wanted.  So here's what I came up with - paste the code below at the end of the init() function in any of OpenLayers' examples:


===================================================

// Get control of the right-click event:
document.getElementById('map').oncontextmenu = function(e){
 e = e?e:window.event;
 if (e.preventDefault) e.preventDefault(); // For non-IE browsers.
 else return false; // For IE browsers.
};


// A control class for capturing click events...
OpenLayers.Control.Click = OpenLayers.Class(OpenLayers.Control, {                

defaultHandlerOptions: {
'single': true,
'double': true,
'pixelTolerance': 0,
'stopSingle': false,
'stopDouble': false
},
handleRightClicks:true,
initialize: function(options) {
this.handlerOptions = OpenLayers.Util.extend(
{}, this.defaultHandlerOptions
);
OpenLayers.Control.prototype.initialize.apply(
this, arguments
); 
this.handler = new OpenLayers.Handler.Click(
this, this.eventMethods, this.handlerOptions
);
},
CLASS_NAME: "OpenLayers.Control.Click"

});


// Add an instance of the Click control that listens to various click events:
var oClick = new OpenLayers.Control.Click({eventMethods:{
'rightclick': function(e) {
alert('rightclick at '+e.xy.x+','+e.xy.y);
},
'click': function(e) {
alert('click at '+e.xy.x+','+e.xy.y);
},
'dblclick': function(e) {
alert('dblclick at '+e.xy.x+','+e.xy.y);
},
'dblrightclick': function(e) {
alert('dblrightclick at '+e.xy.x+','+e.xy.y);
}
}});
map.addControl(oClick);
oClick.activate();

===================================================



The first bit overrides the oncontextmenu event that causes the browser to display a context menu when the right-click event happens.  The next bit creates a simple control class for handling the click events, with the handleRightClicks option enabled.  After that, an instance of the control class is created, with functions for the four different click event types included in the eventMethods option.  Finally, this control is added to the map and activated...any better solutions are welcome.


EDIT: This has been corrected to utilize the 'handleRightClicks' property in the OpenLayers class. Other than this, all that is essentially needed is the overridden oncontextmenu function, which uses preventDefault (for non-IE browsers), or returns false (for IE browsers).

Converting WKT projection info to Proj4 (and back)

I've had many occasions where I've had WKT-style projection text (e.g., the contents of the *.prj files that normally accompany ESRI Shapefiles, or the srtext field in the spatial_ref_sys table in PostGIS), and I needed to get that to an equivalent Proj4-compatible projection string (and vice versa).  I've known for some time there had to be an automated way to do this...as the GDAL/OGR libraries have been doing this internally for some time.  I ran into the issue again today, so I decided to poke around a bit, and found the API functions that are available to do this...I wrote my first ever Python scripts that use the GDAL/OGR python bindings (specifically, OSR).  These scripts simply convert WKT projection text to Proj4 (wkt2proj.py) and back (proj2wkt.py).  Maybe there are some better examples elsewhere...but I didn't quite find what I was looking for.  Since my work is mostly in PHP right now, I'd love to see the GDAL/OGR PHP bindings maintained someday, but this will do for now:

================== wkt2proj.py =======================
#!/usr/bin/env python

import os
import sys
import string
import osgeo.osr

if (len(sys.argv) <> 2):
        print 'Usage: wkt2proj.py [WKT Projection Text]'
else:
        srs = osgeo.osr.SpatialReference()
        srs.ImportFromWkt(sys.argv[1])
        print srs.ExportToProj4()
======================================================

================== proj2wkt.py =======================
#!/usr/bin/env python

import os
import sys
import string
import osgeo.osr

if (len(sys.argv) <> 2):
        print 'Usage: proj2wkt.py [Proj4 Projection Text]'
else:
        srs = osgeo.osr.SpatialReference()
        srs.ImportFromProj4(sys.argv[1])
        print srs.ExportToWkt()
======================================================

On Ubuntu, provided you have the python-gdal package installed, the above files executed by python will accept the first argument as a wkt/proj projection string, and return the proj/wkt equivalent.  For proj2wkt.py, you can also take advantage of known EPSG numbers.  For example, the WKT for the Spherical Mercator projection used by most free online mapping services (e.g., Google Maps, OpenStreetMap, etc) will be returned by: proj2wkt.py '+init=epsg:3857'

I haven't tested this on Windows - but as long as the Proj library is installed and the GDAL Python modules are installed and accessible to the Python environment, I imagine they will work the same.