Maps icon indicating copy to clipboard operation
Maps copied to clipboard

Ability to add custom maptypes with the googlemaps service

Open JeroenDeDauw opened this issue 10 years ago • 7 comments

Import from https://bugzilla.wikimedia.org/show_bug.cgi?id=44869

It would be great to be able to add custom map types to the googlemaps service. In my specific case, for instance, I would like to overlay the map offered by the Norwegian Mapping Authority (Statens Kartverk) alongside Google's terrain map. Documentation for how to do this can be found here: https://developers.google.com/maps/documentation/javascript/maptypes#CustomMapTypes

Using a practical example, with the Norwegian Mapping authority as described, the Javascript code would/could like like this:

function StatkartMapType(name, layer) {
  this.layer = layer
  this.name = name
  this.alt = name
  this.tileSize = new google.maps.Size(256,256);
  this.maxZoom = 19;
  this.getTile = function(coord, zoom, ownerDocument) {
    var div = ownerDocument.createElement('DIV');
    div.style.width = this.tileSize.width + 'px';
    div.style.height = this.tileSize.height + 'px';
    div.style.backgroundImage = "url(http://opencache.statkart.no/gatekeeper/gk/gk.open_gmaps?layers=" + this.layer + "&zoom=" + zoom + "&x=" + coord.x + "&y=" + coord.y + ")";
    return div;
  };
}

var map;

function initialize() {
  var mapOptions = {
    zoom: 8,
    center: new google.maps.LatLng(60,9),
    mapTypeControlOptions: {
      mapTypeIds: ['topo2', google.maps.MapTypeId.TERRAIN]
    }
  };
  map = new google.maps.Map(document.getElementById("map_canvas"), mapOptions);
  map.mapTypes.set('topo2',new StatkartMapType("S.Kartverk", "topo2"));
  map.setMapTypeId('topo2');
}

-- [email protected] 2013-02-11 11:34:08 UTC

JeroenDeDauw avatar Mar 28 '14 10:03 JeroenDeDauw

I'm the one who originally posted this enhancement request, and I finally got around to fork and try to implement this. I have a working version, where an associative array is provided in the options file consisting of the type name as key and the javascript function as value. In all cases the user passes only the type name as parameter in the parser function/tag, but I'm a bit unsure as to which of the following options is the best approach:

  1. Passing the javascript function as a string to the JSON-object that the jquery-code picks up along with the rest of its options-object. This function is simply evaluated with "new " and attached to the maptype registry.
  2. Passing a set of values (name, maxzoom, tilesize, url, zoom parameter name, y parameter name etc.) that will be used to construct the function/object in the jquery code that will be attached to the maptype registry. This avoids passing actual javascript code, but is less clean in other ways.
  3. Injecting the javascript functions from the options file in a script-tag in the html so that they are directly reachable from the jquery code without first being passed as strings in a JSON-object and then evaluated.

The solution I have now uses option 1. I kind of like option 3, but I'm not sure if this breaks any mediawiki or Maps-extension coding styles/best practices. I'm also not completely sure where, code wise, it would be appropriate to do such an injection.

kvolden avatar Mar 23 '15 06:03 kvolden

Hey @kvolden, thanks for looking into this.

I'm very dubious about putting JavaScript function definitions, or indeed, any executable JavaScript code, into PHP variables or JSON. Programatically constructing code in general is suspect.

JeroenDeDauw avatar Mar 25 '15 15:03 JeroenDeDauw

Yes, I agree it's not very pretty. I figured since it's all client side, and only admins with access to Local_settings.php would be able to insert the function, there's no real security risk. There isn't really any significant difference from how OpenLayers are handled (see e.g. line 255 in Maps_settings.php where JS is put in a PHP variable as a string, which has "new " added to it in e.g. line 201 in Maps_DisplayMapRenderer.php, only to be passed to eval() in line 279 in jquery.openlayers.js). But as a matter of principle, I admit it's not very clean.

Anyway, to implement option 2 is perfectly doable. And probably preferable, the more I think about it. I guess that would amount to defining the custom maps in Local_settings.php as an associative array with typename as key and another (probably associative) array as value. There would need to be two values that would be absolutely required (name and url), while the rest would be either optional in the JS or could be defaulted to sensible values. In fact, the original example in the first post could be defined thus in Local_settings.php:

$GLOBALS[egMapsGMaps3CustomMapTypes] = array (
    'nma' => array ('name' => 'Norway Topographic', 'url' => 'http://opencache.statkart.no/gatekeeper/gk/gk.open_gmaps?layers=topo2')
);

Additional values would have to be passed if the parameters of the service does not match the names of the parameters googlemaps uses ("zoom", "x", "y"), or if it's a different tile size, and so on.

kvolden avatar Mar 25 '15 21:03 kvolden

That sounds good

JeroenDeDauw avatar Apr 02 '15 04:04 JeroenDeDauw

@kvolden still want to have this added to the Maps extension?

JeroenDeDauw avatar Jul 05 '18 01:07 JeroenDeDauw

@JeroenDeDauw Sorry, the project where I needed this stalled, but I'm planning on picking it back up when I get time. And yes, the ability to use custom map overlays like described would be very nice (in fact, it is a must-have for that particular project). Are you thinking about implementing it? I don't remember how far I got in my attempt, but I'll see if I can find it when I have some time.

kvolden avatar Jul 05 '18 05:07 kvolden

Oh... I thought you had working code already.

Not planning on implementing this myself but will be happy to review a pull request that adds it.

JeroenDeDauw avatar Jul 05 '18 23:07 JeroenDeDauw